summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:03:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:03:56 +0000
commit18da3ffcd7f3c8a0c5f790c801b5813503c2273d (patch)
tree84caf98dc5cef3d123c56ba12e35fd67026e0693
parentInitial commit. (diff)
downloadkmod-18da3ffcd7f3c8a0c5f790c801b5813503c2273d.tar.xz
kmod-18da3ffcd7f3c8a0c5f790c801b5813503c2273d.zip
Adding upstream version 31+20240202.upstream/31+20240202
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.gitignore33
-rw-r--r--CODING-STYLE69
-rw-r--r--COPYING504
-rw-r--r--Makefile.am418
-rw-r--r--NEWS866
-rw-r--r--README.md130
-rw-r--r--TODO61
-rwxr-xr-xautogen.sh74
-rw-r--r--configure.ac350
-rw-r--r--libkmod/.gitignore6
-rw-r--r--libkmod/COPYING504
-rw-r--r--libkmod/Makefile13
-rw-r--r--libkmod/README58
-rw-r--r--libkmod/docs/.gitignore14
-rw-r--r--libkmod/docs/Makefile.am34
-rw-r--r--libkmod/docs/libkmod-docs.xml27
-rw-r--r--libkmod/docs/libkmod-sections.txt108
-rw-r--r--libkmod/docs/version.xml.in1
-rw-r--r--libkmod/libkmod-builtin.c330
-rw-r--r--libkmod/libkmod-config.c1252
-rw-r--r--libkmod/libkmod-elf.c1219
-rw-r--r--libkmod/libkmod-file.c541
-rw-r--r--libkmod/libkmod-index.c1082
-rw-r--r--libkmod/libkmod-index.h48
-rw-r--r--libkmod/libkmod-internal.h210
-rw-r--r--libkmod/libkmod-list.c314
-rw-r--r--libkmod/libkmod-module.c2986
-rw-r--r--libkmod/libkmod-signature.c358
-rw-r--r--libkmod/libkmod.c1024
-rw-r--r--libkmod/libkmod.h270
-rw-r--r--libkmod/libkmod.pc.in11
-rw-r--r--libkmod/libkmod.sym94
-rw-r--r--m4/.gitignore6
-rw-r--r--m4/attributes.m4306
-rw-r--r--m4/features.m448
-rw-r--r--man/.gitignore4
-rw-r--r--man/Makefile.am31
-rw-r--r--man/depmod.d.xml164
-rw-r--r--man/depmod.xml343
-rw-r--r--man/insmod.xml87
-rw-r--r--man/kmod.xml120
-rw-r--r--man/lsmod.xml73
-rw-r--r--man/modinfo.xml201
-rw-r--r--man/modprobe.d.xml241
-rw-r--r--man/modprobe.xml544
-rw-r--r--man/modules.dep.xml80
-rw-r--r--man/rmmod.xml148
-rw-r--r--shared/.gitignore5
-rw-r--r--shared/array.c113
-rw-r--r--shared/array.h22
-rw-r--r--shared/hash.c341
-rw-r--r--shared/hash.h22
-rw-r--r--shared/macro.h76
-rw-r--r--shared/missing.h59
-rw-r--r--shared/scratchbuf.c60
-rw-r--r--shared/scratchbuf.h31
-rw-r--r--shared/strbuf.c128
-rw-r--r--shared/strbuf.h30
-rw-r--r--shared/util.c548
-rw-r--r--shared/util.h104
-rw-r--r--shell-completion/bash/kmod103
-rw-r--r--testsuite/.gitignore57
-rw-r--r--testsuite/COPYING504
-rw-r--r--testsuite/Makefile15
-rw-r--r--testsuite/README61
-rw-r--r--testsuite/delete_module.c168
-rw-r--r--testsuite/init_module.c403
-rw-r--r--testsuite/module-playground/.gitignore16
-rw-r--r--testsuite/module-playground/Makefile83
-rw-r--r--testsuite/module-playground/Makefile.arch14
-rw-r--r--testsuite/module-playground/README26
-rw-r--r--testsuite/module-playground/cache/mod-fake-cciss.kobin0 -> 5504 bytes
-rw-r--r--testsuite/module-playground/cache/mod-fake-hpsa.kobin0 -> 5264 bytes
-rw-r--r--testsuite/module-playground/cache/mod-fake-scsi-mod.kobin0 -> 4328 bytes
-rw-r--r--testsuite/module-playground/cache/mod-foo-a.kobin0 -> 4392 bytes
-rw-r--r--testsuite/module-playground/cache/mod-foo-b.kobin0 -> 4392 bytes
-rw-r--r--testsuite/module-playground/cache/mod-foo-c.kobin0 -> 4392 bytes
-rw-r--r--testsuite/module-playground/cache/mod-foo.kobin0 -> 3296 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-a.kobin0 -> 4672 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-b.kobin0 -> 4672 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-c.kobin0 -> 4672 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-d.kobin0 -> 4672 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-e.kobin0 -> 4672 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-f.kobin0 -> 194112 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-g.kobin0 -> 194112 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-h.kobin0 -> 197808 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-i.kobin0 -> 197808 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-j.kobin0 -> 197968 bytes
-rw-r--r--testsuite/module-playground/cache/mod-loop-k.kobin0 -> 197808 bytes
-rw-r--r--testsuite/module-playground/cache/mod-simple.kobin0 -> 3472 bytes
-rw-r--r--testsuite/module-playground/dummy.pkcs7bin0 -> 721 bytes
-rw-r--r--testsuite/module-playground/dummy.sha1bin0 -> 102411 bytes
-rw-r--r--testsuite/module-playground/dummy.sha256bin0 -> 102411 bytes
-rw-r--r--testsuite/module-playground/mod-fake-cciss.c37
-rw-r--r--testsuite/module-playground/mod-fake-hpsa.c36
-rw-r--r--testsuite/module-playground/mod-fake-scsi-mod.c21
-rw-r--r--testsuite/module-playground/mod-foo-a.c20
-rw-r--r--testsuite/module-playground/mod-foo-b.c20
-rw-r--r--testsuite/module-playground/mod-foo-c.c20
-rw-r--r--testsuite/module-playground/mod-foo.c23
-rw-r--r--testsuite/module-playground/mod-loop-a.c27
-rw-r--r--testsuite/module-playground/mod-loop-b.c27
-rw-r--r--testsuite/module-playground/mod-loop-c.c25
-rw-r--r--testsuite/module-playground/mod-loop-d.c25
-rw-r--r--testsuite/module-playground/mod-loop-e.c25
-rw-r--r--testsuite/module-playground/mod-loop-f.c24
-rw-r--r--testsuite/module-playground/mod-loop-g.c24
-rw-r--r--testsuite/module-playground/mod-loop-h.c25
-rw-r--r--testsuite/module-playground/mod-loop-i.c25
-rw-r--r--testsuite/module-playground/mod-loop-j.c26
-rw-r--r--testsuite/module-playground/mod-loop-k.c25
-rw-r--r--testsuite/module-playground/mod-loop.h13
-rw-r--r--testsuite/module-playground/mod-simple-i386.kobin0 -> 1988 bytes
-rw-r--r--testsuite/module-playground/mod-simple-sparc64.kobin0 -> 2927 bytes
-rw-r--r--testsuite/module-playground/mod-simple-x86_64.kobin0 -> 2768 bytes
-rw-r--r--testsuite/module-playground/mod-simple.c32
-rw-r--r--testsuite/path.c214
-rw-r--r--testsuite/rootfs-pristine/test-blacklist/etc/modprobe.d/modprobe.conf2
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin0
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep4
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep.binbin0 -> 244 bytes
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.order7
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols4
-rw-r--r--testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols.binbin0 -> 102 bytes
-rw-r--r--testsuite/rootfs-pristine/test-depmod/detect-loop/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-depmod/detect-loop/lib/modules/4.4.4/modules.order0
-rw-r--r--testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/correct-modules.alias37
-rw-r--r--testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.builtin0
-rw-r--r--testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.order7
-rw-r--r--testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.alias37
-rw-r--r--testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.dep3
-rw-r--r--testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.builtin0
-rw-r--r--testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.order7
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/external.conf1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/search.conf1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-external-first/lib/modules/4.4.4/correct-modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/external.conf1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/search.conf1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-external-last/lib/modules/4.4.4/correct-modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/override.conf1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/search.conf1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-override/lib/modules/4.4.4/correct-modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/etc/depmod.d/search.conf1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/lib/modules/4.4.4/correct-modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-simple/etc/depmod.d/search.conf1
-rw-r--r--testsuite/rootfs-pristine/test-depmod/search-order-simple/lib/modules/4.4.4/correct-modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.binbin0 -> 39 bytes
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep0
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin.binbin0 -> 39 bytes
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep0
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin1
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin.binbin0 -> 39 bytes
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep0
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-loaded/correct.txt2
-rw-r--r--testsuite/rootfs-pristine/test-loaded/proc/modules1
l---------testsuite/rootfs-pristine/test-loaded/sys/module/btusb/drivers/usb:btusb1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/holders/.gitignore0
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/initstate1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/notes/.note.gnu.build-idbin0 -> 36 bytes
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/disable_scofix1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/force_scofix1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_csr1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_dga1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_sniffer1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/reset1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/refcnt1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.bss1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.data1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.exit.text1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.gnu.linkonce.this_module1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.init.text1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.note.gnu.build-id1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.11
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.81
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.smp_locks1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.strtab1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.symtab1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.text1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/__param1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/srcversion1
-rw-r--r--testsuite/rootfs-pristine/test-loaded/sys/module/btusb/version1
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-author.txt3
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-depends.txt3
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-description.txt0
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-external.txt1
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-filename.txt3
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-license.txt4
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-parm.txt0
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo-openssl.txt3
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo.txt4
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-sig_key-openssl.txt3
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-sig_key.txt3
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-signer-openssl.txt3
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct-signer.txt3
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/correct.txt24
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep.binbin0 -> 73 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.devname0
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/correct.txt0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/etc/modprobe.d/bogus.conf1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep.binbin0 -> 58 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/alias-to-none/proc/modules0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/correct.txt1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin.binbin0 -> 31 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.softdep2
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep.binbin0 -> 73 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.devname0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/external/proc/modules0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep.binbin0 -> 58 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/force/proc/modules0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/etc/modprobe.d/dumb-instal-loop.conf1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep2
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep.binbin0 -> 118 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols3
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols.binbin0 -> 78 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep.binbin0 -> 73 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.devname0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-abspath/proc/modules0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep.binbin0 -> 73 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.devname0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-from-relpath/proc/modules0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/correct.txt1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep.binbin0 -> 58 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/modules0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/correct.txt4
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/correct.txt6
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/correct.txt6
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep.binbin0 -> 58 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel-force/proc/modules0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep.binbin0 -> 58 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/oldkernel/proc/modules0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/correct-mod-simple.txt1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/correct.txt2
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep3
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep.binbin0 -> 188 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols3
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols.binbin0 -> 78 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/proc/modules2
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/sys/module/mod_loop_a/initstate1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-depends/sys/module/mod_loop_b/initstate1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/show-exports/correct.txt1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/etc/modprobe.d/dumb-softdep.conf1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.builtin.bin0
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep2
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep.binbin0 -> 118 bytes
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols3
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols.binbin0 -> 78 bytes
-rw-r--r--testsuite/rootfs-pristine/test-new-module/from_alias/correct.txt2
-rw-r--r--testsuite/rootfs-pristine/test-new-module/from_alias/etc/modprobe.d/modprobe.conf1
-rw-r--r--testsuite/rootfs-pristine/test-new-module/from_name/correct.txt6
-rw-r--r--testsuite/rootfs-pristine/test-rootfs/lib/modules/a1
-rw-r--r--testsuite/rootfs-pristine/test-rootfs/testdir/.gitignore0
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.builtin0
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.builtin.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.dep.binbin0 -> 58 bytes
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.order0
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.alias1
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.alias.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.builtin0
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.builtin.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.dep1
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.dep.binbin0 -> 58 bytes
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.devname1
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.order0
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.softdep1
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.symbols1
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.symbols.binbin0 -> 12 bytes
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/holders/.gitignore0
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/initstate1
-rw-r--r--testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/refcnt1
-rw-r--r--testsuite/rootfs-pristine/test-util/alias-correct.txt25
-rw-r--r--testsuite/rootfs-pristine/test-util/freadline_wrapped-correct.txt10
-rw-r--r--testsuite/rootfs-pristine/test-util/freadline_wrapped-input.txt8
-rw-r--r--testsuite/rootfs-pristine/test-util2/write-str-safe-correct.txt1
-rwxr-xr-xtestsuite/setup-rootfs.sh181
-rw-r--r--testsuite/stripped-module.h29
-rw-r--r--testsuite/test-array.c201
-rw-r--r--testsuite/test-blacklist.c105
-rw-r--r--testsuite/test-dependencies.c89
-rw-r--r--testsuite/test-depmod.c247
-rw-r--r--testsuite/test-hash.c282
-rw-r--r--testsuite/test-init.c169
-rw-r--r--testsuite/test-initstate.c126
-rw-r--r--testsuite/test-list.c238
-rw-r--r--testsuite/test-loaded.c91
-rw-r--r--testsuite/test-modinfo.c122
-rw-r--r--testsuite/test-modprobe.c469
-rw-r--r--testsuite/test-new-module.c118
-rw-r--r--testsuite/test-scratchbuf.c89
-rw-r--r--testsuite/test-strbuf.c98
-rw-r--r--testsuite/test-testsuite.c163
-rw-r--r--testsuite/test-tools.c71
-rw-r--r--testsuite/test-util.c273
-rw-r--r--testsuite/testsuite.c1119
-rw-r--r--testsuite/testsuite.h191
-rw-r--r--testsuite/uname.c74
-rw-r--r--tools/.gitignore15
-rw-r--r--tools/COPYING339
-rw-r--r--tools/Makefile13
-rw-r--r--tools/depmod.c3153
-rw-r--r--tools/insert.c128
-rw-r--r--tools/insmod.c166
-rw-r--r--tools/kmod.c179
-rw-r--r--tools/kmod.h42
-rw-r--r--tools/kmod.pc.in10
-rw-r--r--tools/log.c149
-rw-r--r--tools/log.h37
-rw-r--r--tools/lsmod.c102
-rw-r--r--tools/modinfo.c500
-rw-r--r--tools/modprobe.c1054
-rw-r--r--tools/remove.c153
-rw-r--r--tools/rmmod.c203
-rw-r--r--tools/static-nodes.c277
455 files changed, 30609 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..29b3102
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,33 @@
+*.o
+*.gcda
+*.gcno
+*.pc
+/*.tar.xz
+/*.md5sum
+/*.mbx
+/*.cover
+.deps/
+.libs/
+/Makefile
+/Makefile.in
+
+/aclocal.m4
+/autom4te.cache/
+/build-aux/
+/config.h
+/config.h.in
+/config.log
+/config.status
+/configure
+/cov-int
+/coverage
+/gtk-doc.make
+/kmod-*.tar.*
+/libtool
+/stamp-h1
+/test-suite.log
+
+*~
+.*.swp
+cscope.out
+tags
diff --git a/CODING-STYLE b/CODING-STYLE
new file mode 100644
index 0000000..cab2b36
--- /dev/null
+++ b/CODING-STYLE
@@ -0,0 +1,69 @@
+Every project has its coding style, and kmod is not an exception. This
+document describes the preferred coding style for kmod code, in order to keep
+some level of consistency among developers so that code can be easily
+understood and maintained, and also to help your code survive under
+maintainer's fastidious eyes so that you can get a passport for your patch
+ASAP.
+
+First of all, kmod coding style must follow every rule for Linux kernel
+(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool
+named checkpatch.pl to help you check the compliance with it. Just type
+"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need
+to clean up all the warnings and errors except this one: "ERROR: Missing
+Signed-off-by: line(s)". kmod does not used Signed-Off lines, so including
+them is actually an error. In certain circumstances one can ignore the 80
+character per line limit. This is generally only allowed if the alternative
+would make the code even less readable.
+
+Besides the kernel coding style above, kmod coding style is heavily based on
+oFono's and BlueZ's. Below some basic rules:
+
+1) Wrap line at 80 char limit.
+
+There are a few exceptions:
+ - Headers may or may not wrap
+ - If it's a string that is hitting the limit, it's preferred not to break
+ in order to be able to grep for that string. E.g:
+
+ err = my_function(ctx, "this is a long string that will pass the 80chr limit");
+
+ - If code would become unreadable if line is wrapped
+ - If there's only one argument to the function, don't put it alone in a
+ new line.
+
+Align the wrapped line either with tabs (BlueZ, oFono, etc) or tab + spaces
+(kernel), at your discretion. Kernel's is preferred.
+
+2) It's better to return/exit early in a function than having a really long
+ "if (...) { }". Example:
+
+ if (x) { // worse | if (!x) // better
+ ... | return b;
+ ... |
+ ... | ...
+ ... | ...
+ ... | ...
+ ... | ...
+ ... | ...
+ ... | ...
+ } else { | ...
+ return b; | return a;
+ } |
+ |
+ return a; |
+
+3) Don't initialize variable unnecessarily
+When declaring a variable, try not to initialize it unless necessary.
+
+Example:
+int i = 1; // wrong
+
+for (i = 0; i < 3; i++) {
+}
+
+4) Let the includes in the following order, separated by a new line:
+ < system headers >
+ < shared/* >
+ < libkmod >
+ < tool >
+ "local headers"
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..8add30a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..6d21ad2
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,418 @@
+SUBDIRS = . libkmod/docs
+
+if BUILD_MANPAGES
+SUBDIRS += man
+endif
+
+DISTCLEAN_LOCAL_HOOKS =
+EXTRA_DIST =
+CLEANFILES = $(BUILT_FILES)
+DISTCLEANFILES =
+BUILT_FILES =
+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
+AM_MAKEFLAGS = --no-print-directory
+
+GCC_COLORS ?= 'yes'
+export GCC_COLORS
+
+AM_CPPFLAGS = \
+ -include $(top_builddir)/config.h \
+ -I$(top_srcdir) \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -DDISTCONFDIR=\""$(distconfdir)"\" \
+ -DMODULE_DIRECTORY=\""$(module_directory)"\" \
+ ${zlib_CFLAGS}
+
+AM_CFLAGS = $(OUR_CFLAGS)
+AM_LDFLAGS = $(OUR_LDFLAGS)
+
+# Rules for libtool versioning (from https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html)
+# 1. Start with version information of ‘0:0:0’ for each libtool library.
+# 2. Update the version information only immediately before a public release of
+# your software. More frequent updates are unnecessary, and only guarantee that
+# the current interface number gets larger faster.
+# 3. If the library source code has changed at all since the last update, then
+# increment revision (‘c:r:a’ becomes ‘c:r+1:a’).
+# 4. If any interfaces have been added, removed, or changed since the last
+# update, increment current, and set revision to 0.
+# 5. If any interfaces have been added since the last public release, then
+# increment age.
+# 6. If any interfaces have been removed or changed since the last public
+# release, then set age to 0.
+LIBKMOD_CURRENT=6
+LIBKMOD_REVISION=1
+LIBKMOD_AGE=4
+
+noinst_LTLIBRARIES = shared/libshared.la
+shared_libshared_la_SOURCES = \
+ shared/macro.h \
+ shared/missing.h \
+ shared/array.c \
+ shared/array.h \
+ shared/hash.c \
+ shared/hash.h \
+ shared/scratchbuf.c \
+ shared/scratchbuf.h \
+ shared/strbuf.c \
+ shared/strbuf.h \
+ shared/util.c \
+ shared/util.h
+
+include_HEADERS = libkmod/libkmod.h
+lib_LTLIBRARIES = libkmod/libkmod.la
+
+libkmod_libkmod_la_SOURCES = \
+ libkmod/libkmod.h \
+ libkmod/libkmod-internal.h \
+ libkmod/libkmod.c \
+ libkmod/libkmod-builtin.c \
+ libkmod/libkmod-list.c \
+ libkmod/libkmod-config.c \
+ libkmod/libkmod-index.c \
+ libkmod/libkmod-index.h \
+ libkmod/libkmod-module.c \
+ libkmod/libkmod-file.c \
+ libkmod/libkmod-elf.c \
+ libkmod/libkmod-signature.c
+
+EXTRA_DIST += libkmod/libkmod.sym
+EXTRA_DIST += libkmod/README \
+ libkmod/COPYING testsuite/COPYING tools/COPYING COPYING
+
+libkmod_libkmod_la_LDFLAGS = $(AM_LDFLAGS) \
+ -version-info $(LIBKMOD_CURRENT):$(LIBKMOD_REVISION):$(LIBKMOD_AGE) \
+ -Wl,--version-script=$(top_srcdir)/libkmod/libkmod.sym
+libkmod_libkmod_la_DEPENDENCIES = \
+ shared/libshared.la \
+ ${top_srcdir}/libkmod/libkmod.sym
+libkmod_libkmod_la_LIBADD = \
+ shared/libshared.la \
+ ${libzstd_LIBS} ${liblzma_LIBS} ${zlib_LIBS} ${libcrypto_LIBS}
+
+noinst_LTLIBRARIES += libkmod/libkmod-internal.la
+libkmod_libkmod_internal_la_SOURCES = $(libkmod_libkmod_la_SOURCES)
+libkmod_libkmod_internal_la_LDFLAGS = $(AM_LDFLAGS) \
+ -Wl,--version-script=$(top_srcdir)/libkmod/libkmod.sym
+libkmod_libkmod_internal_la_DEPENDENCIES = $(libkmod_libkmod_la_DEPENDENCIES)
+libkmod_libkmod_internal_la_LIBADD = $(libkmod_libkmod_la_LIBADD)
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libkmod/libkmod.pc tools/kmod.pc
+
+bashcompletiondir=@bashcompletiondir@
+dist_bashcompletion_DATA = \
+ shell-completion/bash/kmod
+
+install-exec-hook:
+ if test "$(libdir)" != "$(rootlibdir)"; then \
+ $(MKDIR_P) $(DESTDIR)$(rootlibdir) && \
+ so_img_name=$$(readlink $(DESTDIR)$(libdir)/libkmod.so) && \
+ so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \
+ ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libkmod.so && \
+ mv $(DESTDIR)$(libdir)/libkmod.so.* $(DESTDIR)$(rootlibdir); \
+ fi
+
+uninstall-hook:
+ rm -f $(DESTDIR)$(rootlibdir)/libkmod.so*
+
+if BUILD_TOOLS
+bin_PROGRAMS = tools/kmod
+
+noinst_SCRIPTS = \
+ tools/insmod tools/rmmod tools/lsmod \
+ tools/modprobe tools/modinfo tools/depmod
+
+tools_kmod_SOURCES = \
+ tools/kmod.c tools/kmod.h tools/lsmod.c \
+ tools/rmmod.c tools/insmod.c \
+ tools/modinfo.c tools/modprobe.c \
+ tools/depmod.c tools/log.h tools/log.c \
+ tools/static-nodes.c
+
+if BUILD_EXPERIMENTAL
+tools_kmod_SOURCES += \
+ tools/insert.c \
+ tools/remove.c
+endif
+
+tools_kmod_LDADD = \
+ shared/libshared.la \
+ libkmod/libkmod-internal.la
+
+${noinst_SCRIPTS}: tools/kmod
+ $(AM_V_GEN) ($(RM) $@; \
+ $(LN_S) $(notdir $<) $@)
+endif
+
+# ------------------------------------------------------------------------------
+# TESTSUITE
+# ------------------------------------------------------------------------------
+
+EXTRA_DIST += testsuite/setup-rootfs.sh
+
+MODULE_PLAYGROUND = testsuite/module-playground
+ROOTFS = testsuite/rootfs
+ROOTFS_PRISTINE = $(top_srcdir)/testsuite/rootfs-pristine
+CREATE_ROOTFS = $(AM_V_GEN) MODULE_DIRECTORY=$(module_directory) $(top_srcdir)/testsuite/setup-rootfs.sh $(ROOTFS_PRISTINE) $(ROOTFS) $(MODULE_PLAYGROUND) $(top_builddir)/config.h $(sysconfdir)
+
+build-module-playground:
+ $(AM_V_GEN)if test "$(top_srcdir)" != "$(top_builddir)"; then \
+ $(RM) -rf testsuite/module-playground && \
+ mkdir -p testsuite/ && \
+ cp -r $(top_srcdir)/$(MODULE_PLAYGROUND) $(top_builddir)/$(MODULE_PLAYGROUND) && \
+ find $(top_builddir)/$(MODULE_PLAYGROUND) -type d -exec chmod +w {} \; ; \
+ fi
+if BUILD_MODULES
+ $(MAKE) -C $(MODULE_PLAYGROUND)
+else
+ $(MAKE) -C $(MODULE_PLAYGROUND) FAKE_BUILD=1
+endif
+
+
+rootfs: build-module-playground
+ $(CREATE_ROOTFS)
+
+.PHONY: rootfs build-playground
+
+$(ROOTFS): $(ROOTFS_PRISTINE)
+ $(CREATE_ROOTFS)
+
+TESTSUITE_OVERRIDE_LIBS = \
+ testsuite/uname.la testsuite/path.la \
+ testsuite/init_module.la \
+ testsuite/delete_module.la
+TESTSUITE_OVERRIDE_LIBS_LDFLAGS = \
+ avoid-version -module -shared -export-dynamic -rpath /nowhere -ldl
+
+check-am: rootfs
+
+
+EXTRA_DIST += \
+ testsuite/module-playground/cache \
+ testsuite/module-playground/dummy.pkcs7 \
+ testsuite/module-playground/dummy.sha1 \
+ testsuite/module-playground/dummy.sha256 \
+ testsuite/module-playground/Makefile \
+ testsuite/module-playground/Makefile.arch \
+ testsuite/module-playground/mod-fake-cciss.c \
+ testsuite/module-playground/mod-fake-hpsa.c \
+ testsuite/module-playground/mod-fake-scsi-mod.c \
+ testsuite/module-playground/mod-foo-a.c \
+ testsuite/module-playground/mod-foo-b.c \
+ testsuite/module-playground/mod-foo.c \
+ testsuite/module-playground/mod-foo-c.c \
+ testsuite/module-playground/mod-loop-a.c \
+ testsuite/module-playground/mod-loop-b.c \
+ testsuite/module-playground/mod-loop-c.c \
+ testsuite/module-playground/mod-loop-d.c \
+ testsuite/module-playground/mod-loop-e.c \
+ testsuite/module-playground/mod-loop-f.c \
+ testsuite/module-playground/mod-loop-g.c \
+ testsuite/module-playground/mod-loop-h.c \
+ testsuite/module-playground/mod-loop-i.c \
+ testsuite/module-playground/mod-loop-j.c \
+ testsuite/module-playground/mod-loop-k.c \
+ testsuite/module-playground/mod-loop.h \
+ testsuite/module-playground/mod-simple.c \
+ testsuite/module-playground/mod-simple-i386.ko \
+ testsuite/module-playground/mod-simple-sparc64.ko \
+ testsuite/module-playground/mod-simple-x86_64.ko \
+ testsuite/module-playground/README
+
+check_LTLIBRARIES = $(TESTSUITE_OVERRIDE_LIBS)
+
+testsuite_uname_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
+testsuite_path_la_CPPFLAGS = $(AM_CPPFLAGS) \
+ -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\"
+testsuite_path_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
+
+testsuite_delete_module_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
+testsuite_init_module_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS)
+testsuite_init_module_la_SOURCES = testsuite/init_module.c \
+ testsuite/stripped-module.h
+testsuite_init_module_la_LIBADD = libkmod/libkmod-internal.la
+
+TESTSUITE_CPPFLAGS = $(AM_CPPFLAGS) \
+ -DTESTSUITE_ROOTFS=\"$(abs_top_builddir)/$(ROOTFS)/\" \
+ -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\"
+TESTSUITE_LDADD = \
+ testsuite/libtestsuite.la libkmod/libkmod-internal.la \
+ shared/libshared.la
+
+check_LTLIBRARIES += testsuite/libtestsuite.la
+testsuite_libtestsuite_la_SOURCES = \
+ testsuite/testsuite.c testsuite/testsuite.h
+testsuite_libtestsuite_la_DEPENDENCIES = \
+ $(ROOTFS) $(TESTSUITE_OVERRIDE_LIBS)
+testsuite_libtestsuite_la_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_libtestsuite_la_LIBADD = -lrt
+
+TESTSUITE = \
+ testsuite/test-hash \
+ testsuite/test-array \
+ testsuite/test-scratchbuf \
+ testsuite/test-strbuf \
+ testsuite/test-init \
+ testsuite/test-initstate \
+ testsuite/test-testsuite testsuite/test-loaded \
+ testsuite/test-modinfo testsuite/test-util testsuite/test-new-module \
+ testsuite/test-modprobe testsuite/test-blacklist \
+ testsuite/test-dependencies testsuite/test-depmod \
+ testsuite/test-list
+
+if BUILD_EXPERIMENTAL
+TESTSUITE += \
+ testsuite/test-tools
+endif
+
+check_PROGRAMS = $(TESTSUITE)
+TESTS = $(TESTSUITE)
+
+testsuite_test_testsuite_LDADD = \
+ testsuite/libtestsuite.la shared/libshared.la
+testsuite_test_testsuite_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+
+testsuite_test_hash_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_hash_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+
+testsuite_test_array_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_array_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+
+testsuite_test_scratchbuf_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_scratchbuf_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+
+testsuite_test_strbuf_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_strbuf_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+
+testsuite_test_init_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_init_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_initstate_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_initstate_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_loaded_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_loaded_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_modinfo_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_modinfo_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_util_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_util_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_new_module_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_new_module_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_modprobe_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_modprobe_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_blacklist_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_blacklist_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_dependencies_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_dependencies_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_depmod_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_depmod_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_list_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_list_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+
+if BUILD_EXPERIMENTAL
+testsuite_test_tools_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_tools_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+endif
+
+testsuite-distclean:
+ $(RM) -r $(ROOTFS)
+ $(RM) testsuite/stamp-rootfs
+ $(MAKE) -C testsuite/module-playground/ clean
+ if test "$(top_srcdir)" != "$(top_builddir)"; then \
+ $(RM) -rf testsuite/module-playground; \
+ fi
+
+DISTCLEAN_LOCAL_HOOKS += testsuite-distclean
+EXTRA_DIST += testsuite/rootfs-pristine
+
+DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc --sysconfdir=/etc \
+ --with-zlib --with-zstd --with-openssl \
+ --with-bashcompletiondir=$$dc_install_base/$(bashcompletiondir)
+
+distclean-local: $(DISTCLEAN_LOCAL_HOOKS)
+
+buildtest-TESTS:
+ $(MAKE) $(AM_MAKEFLAGS) $(check_LTLIBRARIES) $(check_PROGRAMS)
+
+# ------------------------------------------------------------------------------
+# coverage
+# ------------------------------------------------------------------------------
+
+# .PHONY so it always rebuilds it
+.PHONY: coverage lcov-run lcov-report coverage-sync
+
+# run lcov from scratch, always
+coverage: all
+ $(MAKE) lcov-run
+ $(MAKE) lcov-report
+
+coverage_dir = coverage
+coverage_opts = --base-directory $(srcdir) --directory $(builddir) \
+ --rc 'geninfo_adjust_src_path=$(abspath $(srcdir))=>$(abspath $(builddir))'
+
+if ENABLE_COVERAGE
+# reset run coverage tests
+lcov-run:
+ @rm -rf $(coverage_dir)
+ lcov $(coverage_opts) --zerocounters
+ -$(MAKE) check
+
+# generate report based on current coverage data
+lcov-report:
+ $(MKDIR_P) $(coverage_dir)
+ lcov $(coverage_opts) --capture --no-external --ignore-errors graph \
+ | sed 's|$(abspath $(builddir))|$(abspath $(srcdir))|' > $(coverage_dir)/.lcov.info
+ lcov --remove $(coverage_dir)/.lcov.info --output-file $(coverage_dir)/.lcov-clean.info 'test-*'
+ genhtml -t "kmod test coverage" -o $(coverage_dir) $(coverage_dir)/.lcov-clean.info
+ @echo "Coverage report generated in $(abs_builddir)/$(coverage_dir)/index.html"
+
+else
+
+lcov-run lcov-report:
+ echo "Need to reconfigure with --enable-coverage"
+
+endif
+
+# ------------------------------------------------------------------------------
+# coverity
+# ------------------------------------------------------------------------------
+
+kmod-coverity-%.tar.xz:
+ rm -rf $< cov-int
+ ./autogen.sh c --disable-manpages
+ make clean
+ cov-build --dir cov-int make -j 4
+ tar caf $@ cov-int
+
+coverity-tar: kmod-coverity-$(shell git describe 2>/dev/null).tar.xz
+
+coverity-sync: kmod-coverity-$(shell git describe 2>/dev/null).tar.xz
+ @echo "uploading coverity tarball"
+ @curl --form token=$(COVERITY_KMOD_TOKEN) \
+ --form email=lucas.de.marchi@gmail.com \
+ --form file=@$< \
+ --form version="$(shell git describe)" \
+ --form description="" \
+ https://scan.coverity.com/builds?project=kmod
+
+coverity-clean:
+ rm -rf kmod-coverity-*.tar.xz cov-int
+
+# ------------------------------------------------------------------------------
+# custom release helpers
+# ------------------------------------------------------------------------------
+
+git-release:
+ head -1 NEWS | grep -q "kmod $(VERSION)"
+ git commit -a -m "kmod $(VERSION)"
+ git tag -m "kmod $(VERSION)" -s v$(VERSION)
+ git gc --prune=0
+
+kmod-$(VERSION).tar.xz:
+ make distcheck
+
+kmod-$(VERSION).tar.sign:
+ xz -d -c kmod-$(VERSION).tar.xz | gpg --armor --detach-sign --output kmod-$(VERSION).tar.sign
+
+tar: kmod-$(VERSION).tar.xz kmod-$(VERSION).tar.sign
+
+tar-sync: kmod-$(VERSION).tar.xz kmod-$(VERSION).tar.sign
+ kup put kmod-$(VERSION).tar.xz kmod-$(VERSION).tar.sign /pub/linux/utils/kernel/kmod/
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..00b7f6d
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,866 @@
+kmod 31
+=======
+
+- Improvements
+
+ - Allow passing a path to modprobe so the module is loaded from
+ anywhere from the filesystem, but still handling the module
+ dependencies recorded in the indexes. This is mostly intended for kernel
+ developers to speedup testing their kernel modules without having to load the
+ dependencies manually or override the module in /usr/lib/modules/.
+ Now it's possible to do:
+
+ # modprobe ./drivers/gpu/drm/i915/i915.ko
+
+ As long as the dependencies didn't change, this should do the right thing
+
+ - Use in-kernel decompression if available. This will check the runtime support
+ in the kernel for decompressing modules and use it through finit_module().
+ Previously kmod would fallback to the older init_module() when using
+ compressed modules since there wasn't a way to instruct the kernel to
+ uncompress it on load or check if the kernel supported it or not.
+ This requires a recent kernel (>= 6.4) to have that support and
+ in-kernel decompression properly working in the kernel.
+
+ - Make modprobe fallback to syslog when stderr is not available, as was
+ documented in the man page, but not implemented
+
+ - Better explaing `modprobe -r` and how it differentiates from rmmod
+
+ - depmod learned a `-o <dir>` option to allow using a separate output
+ directory. With this, it's possible to split the output files from
+ the ones used as input from the kernel build system
+
+ - Add compat with glibc >= 2.32.9000 that dropped __xstat
+
+ - Improve testsuite to stop skipping tests when sysconfdir is something
+ other than /etc
+
+ - Build system improvements and updates
+
+ - Change a few return codes from -ENOENT to -ENODATA to avoid confusing output
+ in depmod when the module itself lacks a particular ELF section due to e.g.
+ CONFIG_MODVERSIONS=n in the kernel.
+
+
+- Bug Fixes
+
+ - Fix testsuite using uninitialized memory when testing module removal
+ with --wait
+
+ - Fix testsuite not correctly overriding the stat syscall on 32-bit
+ platforms. For most architectures this was harmless, but for MIPS it
+ was causing some tests to fail.
+
+ - Fix handling unknown signature algorithm
+
+ - Fix linking with a static liblzma, libzstd or zlib
+
+ - Fix memory leak when removing module holders
+
+ - Fix out-of-bounds access when using very long paths as argument to rmmod
+
+ - Fix warnings reported by UBSan
+
+kmod 30
+=======
+
+- Improvements
+ - Stop adding duplicate information on modules.builtin.alias.bin, just use
+ the modules.builtin.bin index
+
+ - Speedup depmod, particularly under qemu with emulated arch, by
+ avoiding a lot of open/read/close of modules.alias.bin. On an
+ emulated ARM rootfs, depmod with only 2 modules was taking ~32s
+ vs ~0.07s now.
+
+ - Add kmod_module_new_from_name_lookup() which allows doing a lookup by
+ module name, without considering the aliases. Other than that search
+ order is similar to kmod_module_new_from_lookup().
+
+ - modinfo learned the --modname option to explicitely show information
+ about the module, even if there is an alias with the same name. This
+ allows showing information about e.g. kernel/lib/crc32.ko, even if
+ kernel also exports a crc32 alias in modules.alias:
+
+ alias crc32 crc32_pclmul
+ alias crc32 crc32_generic
+
+ Same behavior will be used to other modules and to aliases provided
+ by user/distro.
+
+ - depmod.conf learned a new "excludedir" directive so distro/user can
+ configure more directories to be excluded from its search, besides
+ the hardcoded values "build" and "source".
+
+ - Better group modprobe options on help output under "Management, Query and General".
+
+ - modprobe learned a --wait <MSEC> option to be used together with -r
+ when removing a module. This allows modprobe to keep trying the
+ removal if it fails because the module is still in use. An exponential backoff
+ time is used for further retries.
+
+ The wait behavior provided by the kernel when not passing O_NONBLOCK
+ to delete_module() was removed in v3.13 due to not be used and the
+ consequences of having to support it in the kernel. However there may
+ be some users, particularly on testsuites for individual susbsystems, that
+ would want that. So provide a userspace implementation inside modprobe for
+ such users. "rmmod" doesn't have a --wait as it remains a bare minimal over
+ the API provided by the kernel. In future the --wait behavior can be added
+ to libkmod for testsuites not exec'ing modprobe for module removal.
+
+ - kmod_module_remove_module() learned a new flag to silence output when
+ caller wants to handle them - this is particularly important for the
+ --wait flag to modprobe, as it's not desired to keep seeing error messages
+ while waiting for the module to be unused.
+
+ - Add SM3 hash algo support to modinfo output, as already available in the kernel.
+
+- Bug Fixes
+ - Fix modinfo output when showing information for a .ko module when running
+ on a kernel that has that module as builtin.
+
+ - Fix kmod_module_new_from_lookup() returning > 0 rather than 0
+ when it matches an alias.
+
+ - Fix modinfo segfault when module doesn't exist.
+
+ - Add missing function in the html documentation: kmod_get_dirname().
+
+ - Fix modprobe incorrectly handling number of arguments when prepending values from
+ MODPROBE_OPTIONS environment variable.
+
+ - Fix modprobe -r --remove-dependencies and since "dependencies" was a
+ misnomer, add the preferred argument option: "--remove-holders". This
+ is the same name used by the kernel. It allows users to also remove
+ other modules holding the one that is being removed.
+
+ - Fix off-by-one in max module name length in depmod.
+
+- Infra/internal
+ - Start some changes in the out-of-tree test modules in kmod so they are useful
+ for being really inserted in the kernel rather than relying on kmod's mock
+ interface. This helps manual testing and may be used to exercise to test
+ changes in the kernel.
+
+kmod 29
+=======
+
+- Improvements
+ - Add support to use /usr/local as a place for configuration files. This makes it easier
+ to install locally without overriding distro files.
+
+- Bug fixes
+ - Fix `modinfo -F` when module is builtin: when we asked by a specific field from modinfo,
+ it was not working correctly if the module was builtin
+
+ - Documentation fixes on precedence order of /etc and /run: the correct order is
+ /etc/modprobe.d, /run/modprobe.d, /lib/modprobe.d
+
+ - Fix the priority order that we use for searching configuration files. The
+ correct one is /etc, /run, /usr/local/lib, /lib, for both modprobe.d
+ and depmo.d
+
+ - Fix kernel command line parsing when there are quotes present. Grub
+ mangles the command line and changes it from 'module.option="val with
+ spaces"' to '"module.option=val with spaces"'. Although this is weird
+ behavior and grub could have been fixed, the kernel understands it
+ correctly for builtin modules. So change libkmod to also parse it
+ correctly. This also brings another hidden behavior from the kernel:
+ newline in the kernel command line is also allowed and can be used to
+ separate options.
+
+ - Fix a memory leak, overflow and double free on error path
+
+ - Fix documentation for return value from kmod_module_get_info(): we
+ return the number of entries we added to the list
+
+ - Fix output of modules.builtin.alias.bin index: we were writing an empty file due to
+ the misuse of kmod_module_get_info()
+
+- Infra/internal
+ - Retire integration with semaphoreci
+
+ - Declare the github mirror also as an official upstream source: now besides accepting
+ patches via mailing list, PRs on github are also acceptable
+
+ - Misc improvements to testsuite, so we can use it reliably regardless
+ of the configuration used: now tests will skip if we don't have the
+ build dependencies)
+
+kmod 28
+=======
+
+- Improvements
+ - Add Zstandard to the supported compression formats using libzstd
+ (pass --with-zstd to configure)
+
+- Bug fixes
+ - Ignore ill-formed kernel command line, e.g. with "ivrs_acpihid[00:14.5]=AMD0020:0"
+ option in it
+ - Fix some memory leaks
+ - Fix 0-length builtin.alias.bin: it needs at least the index header
+
+kmod 27
+=======
+
+- Improvements
+ - Link to libcrypto rather than requiring openssl
+
+ - Print a better error message when kernel doesn't support module unload
+
+ - Use PKCS#7 instead of CMS for parsing module signature to be
+ compatible with LibreSSL and OpenSSL < 1.1.0
+
+ - Teach modinfo to parse modules.builtin.modinfo. When using Linux kernel
+ >= v5.2-rc1 it's possible to get module information from this new file. Now
+ modinfo is able to show it instead of an error message that the module is
+ built-in:
+
+ Before:
+ $ modinfo ext4
+ modinfo: ERROR: Module ext4 not found.
+
+ After:
+ $ modinfo ext4
+ name: ext4
+ filename: (builtin)
+ softdep: pre: crc32c
+ license: GPL
+ description: Fourth Extended Filesystem
+ author: Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others
+ alias: fs-ext4
+ alias: ext3
+ alias: fs-ext3
+ alias: ext2
+ alias: fs-ext2
+
+- Bug fixes
+ - Do not link python bindings with libpython to be compatible with
+ python3.8
+
+ - Fix module removal with `modprobe -r` when a dependency is built-in.
+ Now it properly ignores them and proceed with removal of other
+ dependencies
+
+ - Fix propagation of return code from install/remove commands to the
+ the probe function. The return values of kmod_module_probe_insert_module()
+ have very specific meanings, do not confuse the caller by return codes
+ from system()
+
+ - Fix softdep config parsing leading to buffer overflow
+
+kmod 26
+=======
+
+- Improvements
+ - Add more error-checking in library functions and remove warnings on newer
+ toolchains
+
+ - Depmod now handles parallel invoctions better by protecting the temporary
+ files being used
+
+ - Improvements to testsuite and added tests to check the our behavior
+ regardless of the features enabled in the kernel, or libraries we link to
+
+ - Teach the --show-exports option to modprobe. This works similarly to
+ --show-modversions, but it reports the exported symbols from that module.
+ Under the hood this reads the .symtab and .strtab section rather than
+ __versions so it shows useful data even if kernel is configured without
+ modversions (CONFIG_MODVERSIONS)
+
+ - Teach pkcs7 parsing to modinfo by using openssl. This allows modinfo to
+ correctly parse the signature appended to a module by the kernel build
+ system when configured with CONFIG_MODULE_SIG_ALL, or when externally
+ signed by the distro. Traditionally modules were signed and a struct
+ was appended together with the signature to the end of the module.
+ This has changed on the kernel for pkcs#7 and now the structure isn't
+ filled out with useful information. So we have to parse the signature
+ block in order to return useful data to the user.
+
+ If kmod is linked with openssl we parse the signature and return the
+ fields as we do for other signatures. An example of the relevant part
+ on the output of modinfo is below:
+
+ Before:
+ sig_id: PKCS#7
+ signer:
+ sig_key:
+ sig_hashalgo: md4
+ After:
+ sig_id: PKCS#7
+ signer: Fedora kernel signing key
+ sig_key: 51:C4:0C:6D:7E:A5:6C:D8:8F:B4:3A:DF:91:78:4F:18:BC:D5:E4:C5
+ sig_hashalgo: sha256
+
+ If kmod is not linked to openssl we just start printing "unknonwn" in the
+ sig_hashalgo field rather than the bogus value.
+
+
+kmod 25
+=======
+
+- Improvements
+ - Add module signature to modinfo output
+
+ - Add support for external directories in depmod: now there's a new
+ "external" keyword parsed by depmod when calculating the dependencies.
+ It allows to add modules to other directories which are not relative
+ to where the modules are commonly installed. This results in
+ modules.dep and friends now understanding absolute paths rather than
+ relative paths only. For more information see depmod.d(1).
+
+ - Add support for CONFIG_MODULE_REL_CRCS
+
+ - Add missing documentation references in man pages
+
+ - Handle the case in which module has a .TOC symbol already while
+ calculating dependencies
+
+ - Improve testsuite and allow to use mkosi to run testsuite in different
+ distros
+
+kmod 24
+=======
+
+- Improvements:
+ - Add more information on dependency loop
+
+ - Sanitize use of strcpy and allow to grow from small strings on stack
+ (common case) to bigger strings on heap when needed
+
+- Bug fixes
+ - Fix wrong dependency loops being reported by depmod
+
+ - Fix crashes when reporting dependency loops
+
+ - Fix parsing kernel command line containing quotes
+
+ - Fix leaks on error paths
+
+kmod 23
+=======
+
+- Improvements:
+ - Don't add comment to modules.devname if it would otherwise be empty
+ to play nice with tools detecting empty files
+
+ - Allow building with BSD sed, that doesn't have -E flag
+
+ - Ignore .TOC. symbols in depmod parsing as it's for PPC64 the
+ equivalent of _GLOBAL_OFFSET_TABLE_
+
+ - Teach modinfo about PKCS#7 module signatures: it doesn't add any
+ other info besides telling the user the module is signed since
+ kernel doesn't add other info on the module section
+
+- Bug fixes
+
+ - Fix -s and -p compat options to insmod triggering force flag
+
+ - Fix long lines from /proc/modules not being handled correctly by
+ kmod_module_new_from_loaded() and kmod_module_get_size() and several
+ other library functions that use them
+
+ - Fix crash on modinfo while checking for available signature of
+ unknown type
+
+ - Fix documentation generation with gtk-doc
+
+kmod 22
+=======
+
+- Tools:
+ - Change defaul log level for tools to WARNING rather than ERROR and update
+ some log levels for current messages
+
+ - depmod doesn't fallback to uname if a bad version is passed in the command
+ line anymore. We just exit with an error.
+
+ - insmod was taught the -f flag, just like in modprobe. It was previously
+ silently ignoring it.
+
+- libkmod
+ - New kmod_get_dirname() API to get the module directory set in the
+ context
+
+- Bug fixes:
+ - Fix return code in error path of kmod_module_insert_module(). We were
+ previously returning ENOSYS rather than ENOENT.
+
+kmod 21
+=======
+
+- New features:
+ - kmod tool started to learn the "insert" and "remove" commands that
+ are the simplified versions of the older modprobe tool. These
+ commands are still work in progress so they are hidden behind a
+ --enable-experimental flag during build. It should not be enabled
+ unless you know what you're doing.
+ - kmod tool now prints the relevant configuration options it was built
+ with when the "--version" argument is passed. This helps to mitigate
+ problems for example when the user is trying to load a compressed
+ module but kmod was built without support for the compression method.
+
+- Improvements to testsuite:
+ - Cache built modules so it is easier to run "make check" on build
+ servers by distro maintainers. If kmod is configured with
+ --disable-test-modules the modules from cache will be used by
+ "make check". No changes to the tests are needed and all of them
+ can run fine.
+
+kmod 20
+=======
+- Bug fixes:
+ - Handle bogus values from ELF, making sure they don't overflow while
+ parsing the file
+ - Fix leak in depmod when -b flag is passed multiple times
+ - Multiple minor fixes from static analysis by coverity and
+ clang-analyze
+ - Fix race between loading modules and checking if it's loaded in the
+ kernel
+
+- New features:
+ - There's a change in behavior regarding builtin modules: we now only
+ consider as builtin those that are present in modules.builtin index.
+ Previously we were also checking the presence of
+ /sys/module/<module-name>, but this is racy and only modules that
+ contain parameters are the ones creating a directory in sysfs.
+
+ Now some commands will start to fail, e.g. "modprobe vt". Since vt
+ can't be compiled as a module it's not present in modules.builtin
+ index. Previously we would report at as builtin, but now we fail
+ because we couldn't find the module.
+
+- Improvements:
+ - Integration of gcov into the build. Currently libkmod is at ~70%
+ covered and tools at ~50% by tests in the testsuite. Utility
+ functions and structures in shared have more than 90% of coverage.
+ - Upload build to coverity
+
+- Improvements to testsuite:
+ - Fix parsing return codes of init_module() calls
+ - Add tests for utility functions in shared/
+ - Add tests for kmod_module_remove_module()
+ - Add playground, in which our own modules are compiled
+ - Port all tests to use modules from module-playground instead of
+ copying prebuilt modules to the repository
+ - Properly handle binaries that exit with no output
+ - Besides comparing the output of commands, allow to copy to
+ stdout/stderr
+
+kmod 19
+=======
+
+- Bug fixes:
+ - Fix missing CLOEXEC in library
+ - Fix error message while opening kmod's index
+
+- New features:
+ - Add kmod(8) man page
+ - Allow to build with libc's without be32toh()
+ - Move code around separating common code and data structures into a
+ shared directory. This allows to share more code between library and
+ tools, making the binary size of tools smaller.
+ - Clarify tools vs library licenses
+ - static-nodes: when writing in tmpfiles format, indicate that
+ creation of static nodes should only happen at boot. This is used and
+ required by systemd-217+.
+
+- Improvements to testsuite:
+ - Add tests for newly created shared/ code
+ - Improve how tests are declared so there's less boilerplate code for
+ each test.
+
+kmod 18
+=======
+
+- Bug fixes:
+ - Fix leaks in error paths
+ - Fix use-after-free in hash implementation causing a wrong index to be
+ generated by depmod with out-of-tree modules
+
+- New features:
+ - Calling depmod with modules creating a dependency loop will now make
+ depmod return an error and not update the indexes. This is to protect
+ the current index not being overridden by another index that may cause
+ a boot failure, depending on the buggy module. It's a necessary
+ change in behavior regarding previous kmod releases and
+ module-init-tools. The error message was also improved to output
+ the modules that caused the dependency cycle.
+
+- Improvements to testsuite:
+ - Fix and improve expected-fail test
+ - Add tests for hashmap implementation
+
+kmod 17
+=======
+
+- Bug fixes:
+ - Fix matching a "." in kernel cmdline, making garbage in the command
+ line be parsed as kmod options
+ - Fix man pages to clarify we don't fallback to parsing modules.dep
+ but instead we depend on modules.dep.bin (generated by depmod) to
+ be present
+ - Fix ELF parsing on 32 bit systems assigning the wrong class.
+ - Fix partial matches of search directives in depmod. Previously having
+ a line in depmod.conf such as "search foo foobar built-in" would cause
+ unpretictable results because foo is a partial match of foobar as well.
+ - Fix unaligned access in modinfo when getting the signature from a
+ module
+ - Make sure softdeps are treated as optional dependencies
+
+- New features:
+ - Accept special files given to "-C" switch in modprobe. This way it's
+ possible to skip system configuration with "modprobe -C /dev/null"
+ - Do not require xsltproc on released tarballs
+ - Don't use Werror anymore
+ - Add experimental python bindings, merged from python-kmod repository
+ (https://github.com/agrover/python-kmod)
+ - Parse softdeps exported by the kernel as
+ /lib/modules/`uname -r`/modules.softdep
+
+- Improvements to testsuite:
+ - Check the list of loaded modules after a test
+
+kmod 16
+=======
+
+- Bug fixes:
+ - Fix usage of readdir_r()
+ - Add some missing checks for memory allocation errors
+
+- New features:
+ - Remove option from libkmod to allow waiting on module removal if
+ the module is being used. It's dangerous since it can block the
+ caller indefinitely.
+ - Improve compatibility with musl libc
+ - Add fallback implementation for compilers without _Static_assert(),
+ e.g. gcc < 4.6
+ - Minor optimizations to the hash table
+ - Make depmod warn if a module has incorrect devname specification
+ - Use cleanup attribute
+
+kmod 15
+=======
+
+- Bug fixes:
+ - kmod static-nodes doesn't fail if modules.devname isn't available
+ - Fix getting boolean parameter from kernel cmdline in case the value
+ is omitted
+ - Fix some mkdir_p() corner cases (used in testsuite and static-nodes)
+
+- New features:
+ - kmod static-nodes creates parent directories if given a -o option
+ - kmod binary statically links to libkmod - if distro is only interested
+ in the kmod tool (for example in an initrd) it can refrain from
+ installing the library
+ - Add shell completion for kmod tool
+
+kmod 14
+=======
+
+- Bug fixes:
+ - Fix some format strings
+ - Protect against NULL being passed around to index
+ - Avoid calling syscall() with -1 when finit_module() is not available,
+ since this doesn't always work
+ - Fix not being able to remove alias due to checking the module's
+ refcount
+ - Minor fixes and refactors
+
+- New features:
+ - Improve libkmod documentation, particularly on how flags are dealt
+ with.
+ - Remove ability to build a static libkmod
+ - Add static-nodes command to kmod that parses modules.devname
+ generating output in useful formats
+
+kmod 13
+=======
+
+- Bug fixes:
+ - Add the long option --symbol-prefix option to depmod (it was absent)
+ and fix its behavior
+ - Don't abort if there's a bogus line in configuration file like "alias
+ psmouse off". Some distros are carrying this since the days of
+ modutils
+
+- New features:
+ - Add support for finit_module(2). If the module is load straight from
+ the disk and without compression we use finit_module() syscall when
+ available, falling back to init_module() otherwise
+ - kmod_module_get_info() also returns the signature if the module is
+ signed and modinfo uses it
+ - Use secure_getenv if available
+ - rmmod understands builtin modules, just like modprobe does
+ - Improve compatibility with musl-libc
+ - Test cases exit with success when receiving a signal if they are
+ xfail tests
+
+kmod 12
+=======
+
+- Bug fixes:
+ - Fix removing vermagic from module when told to force load a module
+ - Fix removing __versions section when told to force load a module: we
+ need to mangle the section header, not the section.
+ - modinfo no longer fails while loading a module from file when path
+ contains ".ko" substring
+
+kmod 11
+=======
+
+- Improvements to testsuite:
+ - Fix testsuite defining symbols twice on 32 bit systems
+ - Allow to check generated files against correct ones
+
+- New features:
+ - libkmod now keeps a file opened after the first call to
+ kmod_module_get_{info,versions,symbols,dependency_symbols}. This
+ reduces significantly the amount of time depmod tool takes to
+ execute. Particularly if compressed modules are used.
+ - Remove --with-rootprefix from build system. It was not a great idea
+ after all and should not be use since it causes more harm then
+ benefits.
+ - Hide --wait option on rmmod. This feature is being targeted for
+ removal from kernel. rmmod still accepts this option, but it's hidden
+ now: man page and usage() say nothing about it and if it's used,
+ user will get a 10s sleep. This way we can check and help if anyone
+ is using this feature.
+ - Refactor message logging on all tools, giving proper prefix, routing
+ everything to syslog when asked for, etc.
+
+- Bug fixes:
+ - Fix parsing of modules.order when using compressed modules
+ - Usage messages go to stdout instead of stderr
+ - Fix memory leak in hash implementation
+
+kmod 10
+=======
+
+- New features:
+ - Read coresize from /sys if supported
+
+ - Add flag to kmod_module_probe_insert() to apply blacklisting during
+ probe only if mod is an alias. Now modprobe uses this flag by default.
+ This is needed to fix a change in behavior regarding module-init-tools
+ and ultimately makes us loading a blacklisted module.
+
+- Better formatting in man pages
+
+- Add option to disable building man pages at build time
+
+- Fixes in the testsuite and refactoring of LDPRELOAD'ed libraries
+
+- Re-licensing testsuite as LGPL
+
+kmod 9
+======
+
+- Improvements to the testsuite:
+ - Check for correct handling of softdep loops
+ - Check for correct handling of install command loops
+
+- Bug fixes:
+ - Fix build with compilers that don't support --gc-sections
+ - Handle errors when dealing with gzipped modules
+ - depmod now handles errors while writing indices, so it doesn't end up
+ with a corrupted index without telling the user
+
+kmod 8
+======
+
+- No new features, small bug fixes only.
+ - Fix a bug in "modprobe -c" output: be compatible with
+ module-init-tools
+
+ - Give a useful error message when init_module fails due to bad
+ parameter or unknown symbols
+
+ - Fix doc generation
+
+kmod 7
+======
+
+- Re-order dirs for configuration files to match the change in systemd and
+ udev: now the priority is:
+ 1. /etc/modprobe.d
+ 2. /run/modprobe.d
+ 3. /lib/modprobe.d
+
+- Fix setting CFLAGS/LDFLAGS in build system. This prevented us from not
+ allowing the user to set his preferences.
+
+- Bug fixes:
+ - Return same error codes of module-init-tools when removing modules
+ with modprobe
+ - Fix builtin output in "--show-depends" when target kernel is not the
+ same of the running kernel
+ - 'modprobe -r' always look at all command line arguments
+ - Fix '-q' usage in modprobe
+
+kmod 6
+======
+
+- New API in libkmod:
+ - kmod_module_apply_filter(): a generic function to apply filters in a
+ list of modules. This deprecates the use of
+ kmod_module_get_filtered_blacklist()
+
+- More tests in testsuite
+
+- Add compatibility with uClibc again
+
+- Lookup modules.builtin.bin to decide if a module is built in kernel
+
+- Downgrade some log messages so we don't annoy people with useless messages
+
+- Bug fixes:
+ - Flag --ignore-loaded was not being properly handled
+ - Infinite loop with softdeps
+ - Infinite loop with dumb user configuration with install commands
+ - Fix leak in index when there's a partial match
+
+- Move repository and tarballs to kernel.org
+
+kmod 5
+======
+
+- Break libkmod's API to insert a module like modprobe does. It now accepts
+ extra an extra argument to print its action and acceptable flags were
+ sanitized.
+
+- Share more code between modprobe and libkmod: using the new version of
+ kmod_module_probe_insert_module() it's possible to share a great amount of
+ code between modprobe and libkmod
+
+- modprobe no longer works with paths: it only accepts module names and/or
+ aliases now.
+
+- testsuite was added to repository, allowing automated tests to be run and
+ easing the way bugs are reproduced.
+
+- modprobe: when dumping configuration ('-c' option) separate config
+ and indexes by adding a commented line between them.
+
+- Fix bugs wrt normalizing aliases and module names
+
+- Fix bug wrt inserting an alias that resolves to multiple modules: we should
+ not stop on the first error, but rather continue to try loading other
+ modules.
+
+- Fix unaligned memory access in hash function, causing depmod to output wrong
+ information in ARMv5
+
+- Fix man page build and install: now they are only installed if tools are
+ enabled
+
+kmod 4
+======
+
+- New APIs in libkmod to:
+ - Get configuration lists: blacklists, install commands, remove
+ commands, aliases, options and softdeps
+ - Dump indexes
+
+- Several bugs fixed in libkmod, modprobe, depmod and modinfo
+
+- API documentation: if configure with run with --enable-gtk-doc, the API doc
+ will be generated by make. Gtk-doc is required for that.
+
+- Man pages are built, which replace man pages from module-init-tools
+
+- 'include' and 'config' options in *.conf files were deprecated
+
+- configure is not run by autogen.sh. Instead, a common set of options is
+ printed. If you are hacking on kmod, consider using bootstrap-configure
+ script.
+
+- 'modprobe -c' works as expected now. As opposed to module-init-tools, it
+ dumps the parsed configuration, not only the file contents.
+
+kmod 3
+======
+
+- New APIs in libkmod to:
+ - Get symbols from module, parsing the ELF section
+ - Get dependency symbols
+ - Check if resources are still valid or if libkmod must be reloaded
+ - Insert module like modprobe, checking (soft-)dependencies, commands,
+ blacklist. It can run commands by itself and to call a callback
+ function.
+
+- Support to load modules compressed with xz
+
+- Tools are now bundled together in a single tool called kmod. It can be
+ called using symlinks with the same names as tools from module-init-tools.
+ E.g: /usr/bin/lsmod -> /usr/bin/kmod. With this we are aiming to complete a
+ 1:1 replacement of module-init-tools.
+
+- The only missing tool, depmod, was added to kmod together with the necessary
+ APIs in libkmod.
+
+- If a program using libkmod runs for a long time, as for example udev, it must
+ check if it doesn't have to re-load libkmod. A new helper function was added
+ in libkmod to check if context is still valid and udev is already using it.
+
+- An 'unaligned access' bug was fixed. So those architecture that does not
+ handle unaligned access can use kmod, too.
+
+kmod 2
+======
+
+Some bugs fixed: the worst of them was with an infinite loop when an alias
+matched more than one module.
+
+- New APIs in libkmod to:
+ - Get soft dependencies
+ - Get info from module files parsing ELF
+ - Get modversions from files parsing ELF
+
+- Support to load gzipped kernel modules: kmod can be compiled with support to
+ gzipped modules by giving the --enable-zlib flag
+
+- Support to forcefully load modules, both vermagic and modversion
+
+- Support to force and nowait removal flags
+
+- Configuration files are parsed in the same order as modprobe: files are
+ sorted alphabetically (independently of their dir) and files with the same
+ name obey a precedence order
+
+- New tool: kmod-modinfo
+
+- kmod-modprobe gained several features to be a 1:1 replacement for modprobe.
+ The only missing things are the options '--showconfig' and '-t / -l'. These
+ last ones have been deprecated long ago and they will be removed from
+ modprobe. A lot of effort has been put on kmod-modprobe to ensure it
+ maintains compabitility with modprobe.
+
+- linux-modules@vger.kernel.org became the official mailing list for kmod
+
+kmod 1
+======
+
+First version of kmod and its library, libkmod.
+
+In the libkmod it's currently possible to:
+ - List modules currently loaded
+ - Get information about loaded modules such as initstate, refcount,
+ holders, sections, address and size
+ - Lookup modules by alias, module name or path
+ - Insert modules: options from configuration and extra options can be
+ passed, but flags are not implemented, yet
+ - Remove modules
+ - Filter list of modules using blacklist
+ - For each module, get the its list of options and install/remove
+ commands
+ - Indexes can be loaded on startup to speedup lookups later
+
+Tools provided with the same set of options as in module-init-tools:
+ - kmod-lsmod
+ - kmod-insmod
+ - kmod-rmmod
+ - kmod-modprobe, with some functionality still missing (use of softdep,
+ dump configuration, show modversions)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9b22bd7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,130 @@
+## kmod - Linux kernel module handling
+
+[![Coverity Scan Status](https://scan.coverity.com/projects/2096/badge.svg)](https://scan.coverity.com/projects/2096)
+
+
+Information
+===========
+
+Mailing list:
+ linux-modules@vger.kernel.org (no subscription needed)
+ https://lore.kernel.org/linux-modules/
+
+Signed packages:
+ http://www.kernel.org/pub/linux/utils/kernel/kmod/
+
+Git:
+ git://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git
+ http://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git
+ https://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git
+
+Gitweb:
+ http://git.kernel.org/?p=utils/kernel/kmod/kmod.git
+ https://github.com/kmod-project/kmod
+
+Irc:
+ #kmod on irc.freenode.org
+
+License:
+ LGPLv2.1+ for libkmod, testsuite and helper libraries
+ GPLv2+ for tools/*
+
+
+OVERVIEW
+========
+
+kmod is a set of tools to handle common tasks with Linux kernel modules like
+insert, remove, list, check properties, resolve dependencies and aliases.
+
+These tools are designed on top of libkmod, a library that is shipped with
+kmod. See libkmod/README for more details on this library and how to use it.
+The aim is to be compatible with tools, configurations and indexes from
+module-init-tools project.
+
+Compilation and installation
+============================
+
+In order to compiler the source code you need following software packages:
+ - GCC compiler
+ - GNU C library
+
+Optional dependencies:
+ - ZLIB library
+ - LZMA library
+ - ZSTD library
+ - OPENSSL library (signature handling in modinfo)
+
+Typical configuration:
+ ./configure CFLAGS="-g -O2" --prefix=/usr \
+ --sysconfdir=/etc --libdir=/usr/lib
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+ make && make install
+
+Hacking
+=======
+
+Run 'autogen.sh' script before configure. If you want to accept the recommended
+flags, you just need to run 'autogen.sh c'.
+
+Make sure to read the CODING-STYLE file and the other READMEs: libkmod/README
+and testsuite/README.
+
+Compatibility with module-init-tools
+====================================
+
+kmod replaces module-init-tools, which is end-of-life. Most of its tools are
+rewritten on top of libkmod so it can be used as a drop in replacements.
+Somethings however were changed. Reasons vary from "the feature was already
+long deprecated on module-init-tools" to "it would be too much trouble to
+support it".
+
+There are several features that are being added in kmod, but we don't
+keep track of them here.
+
+modprobe
+--------
+
+* 'modprobe -l' was marked as deprecated and does not exist anymore
+
+* 'modprobe -t' is gone, together with 'modprobe -l'
+
+* modprobe doesn't parse configuration files with names not ending in
+ '.alias' or '.conf'. modprobe used to warn about these files.
+
+* modprobe doesn't parse 'config' and 'include' commands in configuration
+ files.
+
+* modprobe from m-i-t does not honour softdeps for install commands. E.g.:
+ config:
+
+ install bli "echo bli"
+ install bla "echo bla"
+ softdep bla pre: bli
+
+ With m-i-t, the output of 'modprobe --show-depends bla' will be:
+ install "echo bla"
+
+ While with kmod:
+ install "echo bli"
+ install "echo bla"
+
+* kmod doesn't dump the configuration as is in the config files. Instead it
+ dumps the configuration as it was parsed. Therefore, comments and file names
+ are not dumped, but on the good side we know what the exact configuration
+ kmod is using. We did this because if we only want to know the entire content
+ of configuration files, it's enough to use find(1) in modprobe.d directories
+
+depmod
+------
+
+* there's no 'depmod -m' option: legacy modules.*map files are gone
+
+lsmod
+-----
+
+* module-init-tools used /proc/modules to parse module info. kmod uses
+ /sys/module/*, but there's a fallback to /proc/modules if the latter isn't
+ available
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..3fe06eb
--- /dev/null
+++ b/TODO
@@ -0,0 +1,61 @@
+Features:
+=========
+
+* Add command for signing modules
+ - There is a script to sign modules in kernel tree, but we should be able to
+ sign modules by ourselves
+
+* Stop using NOFAIL() and fatal()
+
+* Protect index against OOM
+
+* Implement actions in kmod tool like 'insert', 'remove', 'info', etc
+
+* testsuite:
+ - when fake delete_module() succeeds, remove its entry from /sys/module
+ - improve coverage (use --enable-coverage to check the current state)
+
+* Stop using system() inside the library and use fork + exec instead
+
+* config: configs that do not need to be matched by fnmatch() could be using a
+ vector instead of a list. This way we could search in it by calling
+ bsearch().
+
+* config: implement the config handling in shared/ and use it in both depmod
+and libkmod
+
+* review API, maybe unify all of these getters:
+ - kmod_module_version_get_symbol()
+ - kmod_module_version_get_crc()
+ - kmod_module_symbol_get_symbol()
+ - kmod_module_symbol_get_crc()
+ - kmod_module_dependency_symbol_get_symbol()
+ - kmod_module_dependency_symbol_get_crc()
+ - kmod_module_versions_free_list()
+ - kmod_module_symbols_free_list()
+ - kmod_module_dependency_symbols_free_list()
+
+* libkmod API breaking changes:
+ - dedicated error value for all kmod_*_get_crc() functions. Currently there
+ is no way for callers to distinguish between a valid CRC=0 and the error
+ code 0.
+
+* index: drop the "open(), seek(), read()" implementation and use another one
+ with mmap(). When lookup() is called and the file is not mmaped, mmap it.
+ Another possibility is to drop the mmap implementation relying on VFS to have
+ the pages cached. This would simplify the interface exported by libkmod.
+ Measure performance before deciding.
+
+* depmod:
+ - join functions for text/binary outputs
+ - use new O_TMPFILE for tmp file creation
+
+Things to be added/removed in kernel (check what is really needed):
+===================================================================
+
+* list of currently loaded modules
+ - readdir() in /sys/modules: dir without a 'initstate' file means the
+ module is builtin.
+
+* kill /proc/modules ?
+ - Unlikely, given other tools might depend on it
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..a7a6022
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+set -e
+
+oldpwd=$(pwd)
+topdir=$(dirname $0)
+cd $topdir
+
+gtkdocize --docdir libkmod/docs || touch libkmod/docs/gtk-doc.make
+autoreconf --force --install --symlink
+
+libdir() {
+ echo $(cd "$1/$(gcc -print-multi-os-directory)"; pwd)
+}
+
+args="\
+--prefix=/usr \
+--sysconfdir=/etc \
+--libdir=$(libdir /usr/lib) \
+"
+
+if [ -f "$topdir/.config.args" ]; then
+ args="$args $(cat $topdir/.config.args)"
+fi
+
+if [ ! -L /bin ]; then
+ args="$args \
+ --with-rootlibdir=$(libdir /lib) \
+ "
+fi
+
+cd $oldpwd
+
+hackargs="\
+--enable-debug \
+--with-zstd \
+--with-xz \
+--with-zlib \
+--with-openssl \
+"
+
+if [ "x$1" = "xc" ]; then
+ shift
+ $topdir/configure CFLAGS='-g -O2' $args $hackargs "$@"
+ make clean
+elif [ "x$1" = "xg" ]; then
+ shift
+ $topdir/configure CFLAGS='-g -Og' $args "$@"
+ make clean
+elif [ "x$1" = "xl" ]; then
+ shift
+ $topdir/configure CC=clang CXX=clang++ $args "$@"
+ make clean
+elif [ "x$1" = "xa" ]; then
+ shift
+ $topdir/configure CFLAGS='-g -O2 -Wsuggest-attribute=pure -Wsuggest-attribute=const' $args "$@"
+ make clean
+elif [ "x$1" = "xs" ]; then
+ shift
+ scan-build $topdir/configure CFLAGS='-g -O0 -std=gnu11' $args "$@"
+ scan-build make
+else
+ echo
+ echo "----------------------------------------------------------------"
+ echo "Initialized build system. For a common configuration please run:"
+ echo "----------------------------------------------------------------"
+ echo
+ echo "$topdir/configure CFLAGS='-g -O2' $args"
+ echo
+ echo If you are debugging or hacking on kmod, consider configuring
+ echo like below:
+ echo
+ echo "$topdir/configure CFLAGS='-g -O2' $args $hackargs"
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..bfabbaa
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,350 @@
+AC_PREREQ(2.64)
+AC_INIT([kmod],
+ [31],
+ [linux-modules@vger.kernel.org],
+ [kmod],
+ [http://git.kernel.org/?p=utils/kernel/kmod/kmod.git])
+
+AC_CONFIG_SRCDIR([libkmod/libkmod.c])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS(config.h)
+AC_CONFIG_AUX_DIR([build-aux])
+
+AC_USE_SYSTEM_EXTENSIONS
+AC_SYS_LARGEFILE
+AC_PREFIX_DEFAULT([/usr])
+AM_MAINTAINER_MODE([enable])
+AM_INIT_AUTOMAKE([check-news foreign 1.11 silent-rules tar-pax no-dist-gzip dist-xz subdir-objects color-tests parallel-tests])
+AM_SILENT_RULES([yes])
+LT_INIT([disable-static pic-only])
+
+AS_IF([test "x$enable_static" = "xyes"], [AC_MSG_ERROR([--enable-static is not supported by kmod])])
+AS_IF([test "x$enable_largefile" = "xno"], [AC_MSG_ERROR([--disable-largefile is not supported by kmod])])
+
+module_compressions=""
+module_signatures="legacy"
+
+#####################################################################
+# Program checks and configurations
+#####################################################################
+
+AC_PROG_SED
+AC_PROG_MKDIR_P
+AC_PROG_LN_S
+PKG_PROG_PKG_CONFIG
+AC_PATH_PROG([XSLTPROC], [xsltproc])
+
+AC_PROG_CC_C99
+
+#####################################################################
+# Function and structure checks
+#####################################################################
+
+AC_CHECK_FUNCS_ONCE(__xstat)
+AC_CHECK_FUNCS_ONCE([__secure_getenv secure_getenv])
+AC_CHECK_FUNCS_ONCE([finit_module])
+
+CC_CHECK_FUNC_BUILTIN([__builtin_clz])
+CC_CHECK_FUNC_BUILTIN([__builtin_types_compatible_p])
+CC_CHECK_FUNC_BUILTIN([__builtin_uaddl_overflow], [ ], [ ])
+CC_CHECK_FUNC_BUILTIN([__builtin_uaddll_overflow], [ ], [ ])
+
+# dietlibc doesn't have st.st_mtim struct member
+AC_CHECK_MEMBERS([struct stat.st_mtim], [], [], [#include <sys/stat.h>])
+
+# musl 1.0 and bionic 4.4 don't have strndupa
+AC_CHECK_DECLS_ONCE([strndupa])
+
+# RHEL 5 and older do not have be32toh
+AC_CHECK_DECLS_ONCE([be32toh])
+
+# Check kernel headers
+AC_CHECK_HEADERS_ONCE([linux/module.h])
+
+AC_MSG_CHECKING([whether _Static_assert() is supported])
+AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE([[_Static_assert(1, "Test");]])],
+ [AC_DEFINE([HAVE_STATIC_ASSERT], [1], [Define if _Static_assert() is available])
+ AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])])
+
+AC_MSG_CHECKING([whether _Noreturn is supported])
+AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE([[#include <stdlib.h>
+ _Noreturn int foo(void) { exit(0); }]])],
+ [AC_DEFINE([HAVE_NORETURN], [1], [Define if _Noreturn is available])
+ AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])])
+
+
+#####################################################################
+# --with-
+#####################################################################
+
+AC_ARG_WITH([distconfdir], AS_HELP_STRING([--with-distconfdir=DIR], [directory to search for distribution configuration files]),
+ [], [with_distconfdir='${prefix}/lib'])
+AC_SUBST([distconfdir], [$with_distconfdir])
+
+AC_ARG_WITH([rootlibdir],
+ AS_HELP_STRING([--with-rootlibdir=DIR], [rootfs directory to install shared libraries]),
+ [], [with_rootlibdir=$libdir])
+AC_SUBST([rootlibdir], [$with_rootlibdir])
+
+# Ideally this would be $prefix/lib/modules but default to /lib/modules for compatibility with earlier versions
+AC_ARG_WITH([module_directory],
+ AS_HELP_STRING([--with-module-directory=DIR], [directory in which to look for kernel modules - typically '/lib/modules' or '${prefix}/lib/modules']),
+ [], [with_module_directory=/lib/modules])
+AC_SUBST([module_directory], [$with_module_directory])
+
+# Check all directory arguments for consistency.
+for ac_var in distconfdir rootlibdir module_directory
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*@<:@^/@:>@\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ @<:@\\/$@:>@* | ?:@<:@\\/@:>@* ) continue;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+AC_ARG_WITH([zstd],
+ AS_HELP_STRING([--with-zstd], [handle Zstandard-compressed modules @<:@default=disabled@:>@]),
+ [], [with_zstd=no])
+AS_IF([test "x$with_zstd" != "xno"], [
+ PKG_CHECK_MODULES([libzstd], [libzstd >= 1.4.4], [LIBS="$LIBS $libzstd_LIBS"])
+ AC_DEFINE([ENABLE_ZSTD], [1], [Enable Zstandard for modules.])
+ module_compressions="zstd $module_compressions"
+], [
+ AC_MSG_NOTICE([Zstandard support not requested])
+])
+CC_FEATURE_APPEND([with_features], [with_zstd], [ZSTD])
+
+AC_ARG_WITH([xz],
+ AS_HELP_STRING([--with-xz], [handle Xz-compressed modules @<:@default=disabled@:>@]),
+ [], [with_xz=no])
+AS_IF([test "x$with_xz" != "xno"], [
+ PKG_CHECK_MODULES([liblzma], [liblzma >= 4.99], [LIBS="$LIBS $liblzma_LIBS"])
+ AC_DEFINE([ENABLE_XZ], [1], [Enable Xz for modules.])
+ module_compressions="xz $module_compressions"
+], [
+ AC_MSG_NOTICE([Xz support not requested])
+])
+CC_FEATURE_APPEND([with_features], [with_xz], [XZ])
+
+AC_ARG_WITH([zlib],
+ AS_HELP_STRING([--with-zlib], [handle gzipped modules @<:@default=disabled@:>@]),
+ [], [with_zlib=no])
+AS_IF([test "x$with_zlib" != "xno"], [
+ PKG_CHECK_MODULES([zlib], [zlib], [LIBS="$LIBS $zlib_LIBS"])
+ AC_DEFINE([ENABLE_ZLIB], [1], [Enable zlib for modules.])
+ module_compressions="gzip $module_compressions"
+], [
+ AC_MSG_NOTICE([zlib support not requested])
+])
+CC_FEATURE_APPEND([with_features], [with_zlib], [ZLIB])
+
+AC_ARG_WITH([openssl],
+ AS_HELP_STRING([--with-openssl], [handle PKCS7 signatures @<:@default=disabled@:>@]),
+ [], [with_openssl=no])
+AS_IF([test "x$with_openssl" != "xno"], [
+ PKG_CHECK_MODULES([libcrypto], [libcrypto >= 1.1.0], [LIBS="$LIBS $libcrypto_LIBS"])
+ AC_DEFINE([ENABLE_OPENSSL], [1], [Enable openssl for modinfo.])
+ module_signatures="PKCS7 $module_signatures"
+], [
+ AC_MSG_NOTICE([openssl support not requested])
+])
+CC_FEATURE_APPEND([with_features], [with_openssl], [LIBCRYPTO])
+
+AC_ARG_WITH([bashcompletiondir],
+ AS_HELP_STRING([--with-bashcompletiondir=DIR], [Bash completions directory]),
+ [],
+ [AS_IF([$($PKG_CONFIG --exists bash-completion)], [
+ with_bashcompletiondir=$($PKG_CONFIG --variable=completionsdir bash-completion)
+ ] , [
+ with_bashcompletiondir=${datadir}/bash-completion/completions
+ ])])
+AC_SUBST([bashcompletiondir], [$with_bashcompletiondir])
+
+#####################################################################
+# --enable-
+#####################################################################
+
+AC_ARG_ENABLE([experimental],
+ AS_HELP_STRING([--enable-experimental], [enable experimental tools and features. Do not enable it unless you know what you are doing. @<:@default=disabled@:>@]),
+ [], enable_experimental=no)
+AM_CONDITIONAL([BUILD_EXPERIMENTAL], [test "x$enable_experimental" = "xyes"])
+AS_IF([test "x$enable_experimental" = "xyes"], [
+ AC_DEFINE(ENABLE_EXPERIMENTAL, [1], [Experimental features.])
+])
+CC_FEATURE_APPEND([with_features], [enable_experimental], [EXPERIMENTAL])
+
+AC_ARG_ENABLE([tools],
+ AS_HELP_STRING([--disable-tools], [disable building tools that provide same functionality as module-init-tools @<:@default=enabled@:>@]),
+ [], enable_tools=yes)
+AM_CONDITIONAL([BUILD_TOOLS], [test "x$enable_tools" = "xyes"])
+
+AC_ARG_ENABLE([manpages],
+ AS_HELP_STRING([--disable-manpages], [disable manpages @<:@default=enabled@:>@]),
+ [], enable_manpages=yes)
+AM_CONDITIONAL([BUILD_MANPAGES], [test "x$enable_manpages" = "xyes"])
+
+AC_ARG_ENABLE([test-modules],
+ AS_HELP_STRING([--disable-test-modules], [disable building test modules during make check: cached modules will be used @<:@default=enabled@:>@]),
+ [], enable_test_modules=yes)
+AM_CONDITIONAL([BUILD_MODULES], [test "x$enable_test_modules" = "xyes"])
+
+AC_ARG_ENABLE([logging],
+ AS_HELP_STRING([--disable-logging], [disable system logging @<:@default=enabled@:>@]),
+ [], enable_logging=yes)
+AS_IF([test "x$enable_logging" = "xyes"], [
+ AC_DEFINE(ENABLE_LOGGING, [1], [System logging.])
+])
+
+AC_ARG_ENABLE([debug],
+ AS_HELP_STRING([--enable-debug], [enable debug messages @<:@default=disabled@:>@]),
+ [], [enable_debug=no])
+AS_IF([test "x$enable_debug" = "xyes"], [
+ AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.])
+])
+
+AC_ARG_ENABLE([coverage],
+ AS_HELP_STRING([--enable-coverage], [enable test coverage @<:@default=disabled@:>@]),
+ [], [enable_coverage=no])
+AS_IF([test "x$enable_coverage" = "xyes"], [
+ AC_CHECK_PROG(have_coverage, [lcov], [yes], [no])
+ AS_IF([test "x$have_coverage" = xno],[
+ AC_MSG_ERROR([*** lcov support requested but the program was not found])
+ ], [
+ lcov_version_major="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 1`"
+ lcov_version_minor="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 2`"
+ AS_IF([test "$lcov_version_major" -lt 1 -o "$lcov_version_minor" -lt 10], [
+ AC_MSG_ERROR([*** lcov version is too old. 1.10 required])
+ ], [
+ have_coverage=yes
+ CC_CHECK_FLAGS_APPEND([with_coverage_cflags], [CFLAGS], [\
+ -fprofile-arcs \
+ -ftest-coverage])
+ ])
+ ])
+])
+AM_CONDITIONAL([ENABLE_COVERAGE], [test "x$enable_coverage" = "xyes"])
+
+m4_ifdef([GTK_DOC_CHECK], [
+GTK_DOC_CHECK([1.14],[--flavour no-tmpl-flat])
+], [
+AM_CONDITIONAL([ENABLE_GTK_DOC], false)])
+
+#####################################################################
+# Default CFLAGS and LDFLAGS
+#####################################################################
+
+CC_CHECK_FLAGS_APPEND(with_cflags, [CFLAGS], [\
+ -pipe \
+ -DANOTHER_BRICK_IN_THE \
+ -Wall \
+ -W \
+ -Wextra \
+ -Wno-inline \
+ -Wvla \
+ -Wundef \
+ -Wformat=2 \
+ -Wlogical-op \
+ -Wsign-compare \
+ -Wformat-security \
+ -Wmissing-include-dirs \
+ -Wformat-nonliteral \
+ -Wold-style-definition \
+ -Wpointer-arith \
+ -Winit-self \
+ -Wdeclaration-after-statement \
+ -Wfloat-equal \
+ -Wmissing-prototypes \
+ -Wstrict-prototypes \
+ -Wredundant-decls \
+ -Wmissing-declarations \
+ -Wmissing-noreturn \
+ -Wshadow \
+ -Wendif-labels \
+ -Wstrict-aliasing=3 \
+ -Wwrite-strings \
+ -Wno-long-long \
+ -Wno-overlength-strings \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -Wno-unused-result \
+ -Wnested-externs \
+ -Wchar-subscripts \
+ -Wtype-limits \
+ -Wuninitialized \
+ -fno-common \
+ -fdiagnostics-show-option \
+ -fvisibility=hidden \
+ -ffunction-sections \
+ -fdata-sections])
+AC_SUBST([OUR_CFLAGS], "$with_cflags $with_coverage_cflags")
+
+
+CC_CHECK_FLAGS_APPEND([with_ldflags], [LDFLAGS], [ \
+ -Wl,--as-needed \
+ -Wl,--no-undefined \
+ -Wl,--gc-sections])
+AC_SUBST([OUR_LDFLAGS], $with_ldflags)
+
+AC_DEFINE_UNQUOTED(KMOD_FEATURES, ["$with_features"], [Features in this build])
+
+#####################################################################
+# Generate files from *.in
+#####################################################################
+
+AC_SUBST([module_compressions], $module_compressions)
+AC_SUBST([module_signatures], $module_signatures)
+
+AC_CONFIG_FILES([
+ Makefile
+ man/Makefile
+ libkmod/docs/Makefile
+ libkmod/docs/version.xml
+ libkmod/libkmod.pc
+ tools/kmod.pc
+])
+
+
+#####################################################################
+
+AC_OUTPUT
+AC_MSG_RESULT([
+ $PACKAGE $VERSION
+ =======
+
+ module_directory: ${module_directory}
+ prefix: ${prefix}
+ sysconfdir: ${sysconfdir}
+ distconfdir: ${distconfdir}
+ libdir: ${libdir}
+ rootlibdir: ${rootlibdir}
+ includedir: ${includedir}
+ bindir: ${bindir}
+ Bash completions dir: ${with_bashcompletiondir}
+
+ compiler: ${CC}
+ cflags: ${with_cflags} ${CFLAGS}
+ ldflags: ${with_ldflags} ${LDFLAGS}
+
+ experimental features: ${enable_experimental}
+ tools: ${enable_tools}
+ logging: ${enable_logging}
+ compression: zstd=${with_zstd} xz=${with_xz} zlib=${with_zlib}
+ debug: ${enable_debug}
+ coverage: ${enable_coverage}
+ doc: ${enable_gtk_doc}
+ man: ${enable_manpages}
+ test-modules: ${enable_test_modules}
+
+ features: ${with_features}
+])
diff --git a/libkmod/.gitignore b/libkmod/.gitignore
new file mode 100644
index 0000000..826fd62
--- /dev/null
+++ b/libkmod/.gitignore
@@ -0,0 +1,6 @@
+.dirstamp
+.deps/
+.libs/
+*.la
+*.lo
+libkmod.pc
diff --git a/libkmod/COPYING b/libkmod/COPYING
new file mode 100644
index 0000000..8add30a
--- /dev/null
+++ b/libkmod/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/libkmod/Makefile b/libkmod/Makefile
new file mode 100644
index 0000000..223bec2
--- /dev/null
+++ b/libkmod/Makefile
@@ -0,0 +1,13 @@
+# Copyright 2010 Lennart Poettering
+#
+# This file has been copied from systemd. It is a dirty trick to simplify
+# compilation when CWD is not the root of the source tree. This file is not
+# intended to be distributed. So, don't touch it, even better ignore it!
+
+all:
+ $(MAKE) -C ..
+
+clean:
+ $(MAKE) -C .. clean
+
+.PHONY: all clean
diff --git a/libkmod/README b/libkmod/README
new file mode 100644
index 0000000..3e1c8dc
--- /dev/null
+++ b/libkmod/README
@@ -0,0 +1,58 @@
+libkmod - linux kernel module handling library
+
+ABSTRACT
+========
+
+libkmod was created to allow programs to easily insert, remove and
+list modules, also checking its properties, dependencies and aliases.
+
+there is no shared/global context information and it can be used by
+multiple sites on a single program, also being able to be used from
+threads, although it's not thread safe (you must lock explicitly).
+
+
+OVERVIEW
+========
+
+Every user should create and manage it's own library context with:
+
+ struct kmod_ctx *ctx = kmod_new(kernel_dirname);
+ kmod_unref(ctx);
+
+
+Modules can be created by various means:
+
+ struct kmod_module *mod;
+ int err;
+
+ err = kmod_module_new_from_path(ctx, path, &mod);
+ if (err < 0) {
+ /* code */
+ } else {
+ /* code */
+ kmod_module_unref(mod);
+ }
+
+ err = kmod_module_new_from_name(ctx, name, &mod);
+ if (err < 0) {
+ /* code */
+ } else {
+ /* code */
+ kmod_module_unref(mod);
+ }
+
+
+Or could be resolved from a known alias to a list of alternatives:
+
+ struct kmod_list *list, *itr;
+ int err;
+ err = kmod_module_new_from_lookup(ctx, alias, &list);
+ if (err < 0) {
+ /* code */
+ } else {
+ kmod_list_foreach(itr, list) {
+ struct kmod_module *mod = kmod_module_get_module(itr);
+ /* code */
+ }
+ }
+
diff --git a/libkmod/docs/.gitignore b/libkmod/docs/.gitignore
new file mode 100644
index 0000000..7514b08
--- /dev/null
+++ b/libkmod/docs/.gitignore
@@ -0,0 +1,14 @@
+*.bak
+*.stamp
+*.sgml
+libkmod.*
+libkmod-*.xml
+!libkmod-docs.xml
+libkmod-*.txt
+!libkmod-sections.txt
+version.xml
+xml
+html
+gtk-doc.make
+Makefile
+Makefile.in
diff --git a/libkmod/docs/Makefile.am b/libkmod/docs/Makefile.am
new file mode 100644
index 0000000..c4f3d69
--- /dev/null
+++ b/libkmod/docs/Makefile.am
@@ -0,0 +1,34 @@
+
+AUTOMAKE_OPTIONS = 1.11
+
+DOC_MODULE = libkmod
+
+DOC_MODULE_VERSION = 3
+
+DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml
+
+DOC_SOURCE_DIR = $(top_srcdir)/libkmod
+
+SCAN_OPTIONS = --ignore-decorators="__must_check|KMOD_EXPORT"
+
+MKDB_OPTIONS = --xml-mode --output-format=xml --name-space kmod --tmpl-dir=.
+
+MKTMPL_OPTIONS = --output-dir=.
+
+MKHTML_OPTIONS = --path=$(abs_srcdir)/doc --path=$(abs_builddir)/doc
+
+HFILE_GLOB = $(top_srcdir)/libkmod/libkmod.h
+CFILE_GLOB = $(top_srcdir)/libkmod/libkmod.c $(top_srcdir)/libkmod/libkmod-module.c $(top_srcdir)/libkmod/libkmod-list.c
+
+IGNORE_HFILES = libkmod-internal.h \
+ libkmod-index.h
+
+content_files = version.xml
+
+EXTRA_DIST =
+
+if ENABLE_GTK_DOC
+include $(top_srcdir)/libkmod/docs/gtk-doc.make
+else
+EXTRA_DIST += libkmod-docs.xml libkmod-sections.txt
+endif
diff --git a/libkmod/docs/libkmod-docs.xml b/libkmod/docs/libkmod-docs.xml
new file mode 100644
index 0000000..fd17506
--- /dev/null
+++ b/libkmod/docs/libkmod-docs.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+ <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+ <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index">
+ <bookinfo>
+ <title>libkmod Reference Manual</title>
+ <releaseinfo>for libkmod version &version;</releaseinfo>
+ </bookinfo>
+
+ <chapter>
+ <title>libkmod</title>
+ <xi:include href="xml/libkmod.xml"/>
+ <xi:include href="xml/libkmod-list.xml"/>
+ <xi:include href="xml/libkmod-config.xml"/>
+ <xi:include href="xml/libkmod-module.xml"/>
+ <xi:include href="xml/libkmod-loaded.xml"/>
+ </chapter>
+
+ <index id="api-index-full">
+ <title>API Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+</book>
diff --git a/libkmod/docs/libkmod-sections.txt b/libkmod/docs/libkmod-sections.txt
new file mode 100644
index 0000000..33d9eec
--- /dev/null
+++ b/libkmod/docs/libkmod-sections.txt
@@ -0,0 +1,108 @@
+<SECTION>
+<FILE>libkmod</FILE>
+kmod_ctx
+kmod_new
+kmod_ref
+kmod_unref
+
+kmod_load_resources
+kmod_unload_resources
+kmod_validate_resources
+kmod_dump_index
+
+kmod_set_log_priority
+kmod_get_log_priority
+kmod_set_log_fn
+kmod_get_userdata
+kmod_set_userdata
+kmod_get_dirname
+</SECTION>
+
+<SECTION>
+<FILE>libkmod-list</FILE>
+kmod_list
+kmod_list_foreach
+kmod_list_foreach_reverse
+kmod_list_last
+kmod_list_next
+kmod_list_prev
+</SECTION>
+
+<SECTION>
+<FILE>libkmod-config</FILE>
+kmod_config_iter
+kmod_config_get_blacklists
+kmod_config_get_install_commands
+kmod_config_get_remove_commands
+kmod_config_get_aliases
+kmod_config_get_options
+kmod_config_get_softdeps
+kmod_config_iter_get_key
+kmod_config_iter_get_value
+kmod_config_iter_next
+kmod_config_iter_free_iter
+</SECTION>
+
+<SECTION>
+<FILE>libkmod-module</FILE>
+kmod_module
+kmod_module_new_from_lookup
+kmod_module_new_from_name_lookup
+kmod_module_new_from_name
+kmod_module_new_from_path
+
+kmod_module_ref
+kmod_module_unref
+kmod_module_unref_list
+
+kmod_module_insert_module
+kmod_module_probe_insert_module
+kmod_module_remove_module
+
+kmod_module_get_module
+kmod_module_get_dependencies
+kmod_module_get_softdeps
+kmod_module_apply_filter
+kmod_module_get_filtered_blacklist
+kmod_module_get_install_commands
+kmod_module_get_remove_commands
+kmod_module_get_name
+kmod_module_get_options
+kmod_module_get_path
+
+kmod_module_get_dependency_symbols
+kmod_module_dependency_symbol_get_bind
+kmod_module_dependency_symbol_get_crc
+kmod_module_dependency_symbol_get_symbol
+kmod_module_dependency_symbols_free_list
+
+kmod_module_get_sections
+kmod_module_section_free_list
+kmod_module_section_get_address
+kmod_module_section_get_name
+
+kmod_module_get_symbols
+kmod_module_symbol_get_crc
+kmod_module_symbol_get_symbol
+kmod_module_symbols_free_list
+
+kmod_module_get_versions
+kmod_module_version_get_crc
+kmod_module_version_get_symbol
+kmod_module_versions_free_list
+
+kmod_module_get_info
+kmod_module_info_free_list
+kmod_module_info_get_key
+kmod_module_info_get_value
+</SECTION>
+
+<SECTION>
+<FILE>libkmod-loaded</FILE>
+kmod_module_new_from_loaded
+kmod_module_get_initstate
+kmod_module_initstate_str
+kmod_module_get_size
+kmod_module_get_refcnt
+kmod_module_get_holders
+</SECTION>
diff --git a/libkmod/docs/version.xml.in b/libkmod/docs/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/libkmod/docs/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/libkmod/libkmod-builtin.c b/libkmod/libkmod-builtin.c
new file mode 100644
index 0000000..65334a8
--- /dev/null
+++ b/libkmod/libkmod-builtin.c
@@ -0,0 +1,330 @@
+/*
+ * libkmod - interface to kernel built-in modules
+ *
+ * Copyright (C) 2019 Alexey Gladkov <gladkov.alexey@gmail.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+#define MODULES_BUILTIN_MODINFO "modules.builtin.modinfo"
+
+struct kmod_builtin_iter {
+ struct kmod_ctx *ctx;
+
+ // The file descriptor.
+ int file;
+
+ // The total size in bytes.
+ ssize_t size;
+
+ // The offset of current module.
+ off_t pos;
+
+ // The offset at which the next module is located.
+ off_t next;
+
+ // Number of strings in the current block.
+ ssize_t nstrings;
+
+ // Internal buffer and its size.
+ size_t bufsz;
+ char *buf;
+};
+
+static struct kmod_builtin_iter *kmod_builtin_iter_new(struct kmod_ctx *ctx)
+{
+ char path[PATH_MAX];
+ int file, sv_errno;
+ struct stat sb;
+ struct kmod_builtin_iter *iter = NULL;
+ const char *dirname = kmod_get_dirname(ctx);
+ size_t len = strlen(dirname);
+
+ file = -1;
+
+ if ((len + 1 + strlen(MODULES_BUILTIN_MODINFO) + 1) >= PATH_MAX) {
+ sv_errno = ENAMETOOLONG;
+ goto fail;
+ }
+
+ snprintf(path, PATH_MAX, "%s/%s", dirname, MODULES_BUILTIN_MODINFO);
+
+ file = open(path, O_RDONLY|O_CLOEXEC);
+ if (file < 0) {
+ sv_errno = errno;
+ goto fail;
+ }
+
+ if (fstat(file, &sb) < 0) {
+ sv_errno = errno;
+ goto fail;
+ }
+
+ iter = malloc(sizeof(*iter));
+ if (!iter) {
+ sv_errno = ENOMEM;
+ goto fail;
+ }
+
+ iter->ctx = ctx;
+ iter->file = file;
+ iter->size = sb.st_size;
+ iter->nstrings = 0;
+ iter->pos = 0;
+ iter->next = 0;
+ iter->bufsz = 0;
+ iter->buf = NULL;
+
+ return iter;
+fail:
+ if (file >= 0)
+ close(file);
+
+ errno = sv_errno;
+
+ return iter;
+}
+
+static void kmod_builtin_iter_free(struct kmod_builtin_iter *iter)
+{
+ close(iter->file);
+ free(iter->buf);
+ free(iter);
+}
+
+static off_t get_string(struct kmod_builtin_iter *iter, off_t offset,
+ char **line, size_t *size)
+{
+ int sv_errno;
+ char *nullp = NULL;
+ size_t linesz = 0;
+
+ while (!nullp) {
+ char buf[BUFSIZ];
+ ssize_t sz;
+ size_t partsz;
+
+ sz = pread(iter->file, buf, BUFSIZ, offset);
+ if (sz < 0) {
+ sv_errno = errno;
+ goto fail;
+ } else if (sz == 0) {
+ offset = 0;
+ break;
+ }
+
+ nullp = memchr(buf, '\0', (size_t) sz);
+ partsz = (size_t)((nullp) ? (nullp - buf) + 1 : sz);
+ offset += (off_t) partsz;
+
+ if (iter->bufsz < linesz + partsz) {
+ iter->bufsz = linesz + partsz;
+ iter->buf = realloc(iter->buf, iter->bufsz);
+
+ if (!iter->buf) {
+ sv_errno = errno;
+ goto fail;
+ }
+ }
+
+ strncpy(iter->buf + linesz, buf, partsz);
+ linesz += partsz;
+ }
+
+ if (linesz) {
+ *line = iter->buf;
+ *size = linesz;
+ }
+
+ return offset;
+fail:
+ errno = sv_errno;
+ return -1;
+}
+
+static bool kmod_builtin_iter_next(struct kmod_builtin_iter *iter)
+{
+ char *line, *modname;
+ size_t linesz;
+ off_t pos, offset, modlen;
+
+ modname = NULL;
+
+ iter->nstrings = 0;
+ offset = pos = iter->next;
+
+ while (offset < iter->size) {
+ char *dot;
+ off_t len;
+
+ offset = get_string(iter, pos, &line, &linesz);
+ if (offset <= 0) {
+ if (offset)
+ ERR(iter->ctx, "get_string: %s\n", strerror(errno));
+ pos = iter->size;
+ break;
+ }
+
+ dot = strchr(line, '.');
+ if (!dot) {
+ ERR(iter->ctx, "kmod_builtin_iter_next: unexpected string without modname prefix\n");
+ pos = iter->size;
+ break;
+ }
+
+ len = dot - line;
+
+ if (!modname) {
+ modname = strdup(line);
+ modlen = len;
+ } else if (modlen != len || strncmp(modname, line, len)) {
+ break;
+ }
+
+ iter->nstrings++;
+ pos = offset;
+ }
+
+ iter->pos = iter->next;
+ iter->next = pos;
+
+ free(modname);
+
+ return (iter->pos < iter->size);
+}
+
+static bool kmod_builtin_iter_get_modname(struct kmod_builtin_iter *iter,
+ char modname[static PATH_MAX])
+{
+ int sv_errno;
+ char *line, *dot;
+ size_t linesz, len;
+ off_t offset;
+
+ if (iter->pos == iter->size)
+ return false;
+
+ line = NULL;
+
+ offset = get_string(iter, iter->pos, &line, &linesz);
+ if (offset <= 0) {
+ sv_errno = errno;
+ if (offset)
+ ERR(iter->ctx, "get_string: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ dot = strchr(line, '.');
+ if (!dot) {
+ sv_errno = errno;
+ ERR(iter->ctx, "kmod_builtin_iter_get_modname: unexpected string without modname prefix\n");
+ goto fail;
+ }
+
+ len = dot - line;
+
+ if (len >= PATH_MAX) {
+ sv_errno = ENAMETOOLONG;
+ goto fail;
+ }
+
+ strncpy(modname, line, len);
+ modname[len] = '\0';
+
+ return true;
+fail:
+ errno = sv_errno;
+ return false;
+}
+
+/* array will be allocated with strings in a single malloc, just free *array */
+ssize_t kmod_builtin_get_modinfo(struct kmod_ctx *ctx, const char *modname,
+ char ***modinfo)
+{
+ ssize_t count = 0;
+ char *s, *line = NULL;
+ size_t i, n, linesz, modlen, size;
+ off_t pos, offset;
+
+ char *name = NULL;
+ char buf[PATH_MAX];
+
+ struct kmod_builtin_iter *iter = kmod_builtin_iter_new(ctx);
+
+ if (!iter)
+ return -errno;
+
+ while (!name && kmod_builtin_iter_next(iter)) {
+ if (!kmod_builtin_iter_get_modname(iter, buf)) {
+ count = -errno;
+ goto fail;
+ }
+
+ if (strcmp(modname, buf))
+ continue;
+
+ name = buf;
+ }
+
+ if (!name) {
+ count = -ENOSYS;
+ goto fail;
+ }
+
+ modlen = strlen(modname) + 1;
+ count = iter->nstrings;
+ size = iter->next - iter->pos - (modlen * count);
+
+ *modinfo = malloc(size + sizeof(char *) * (count + 1));
+ if (!*modinfo) {
+ count = -errno;
+ goto fail;
+ }
+
+ s = (char *)(*modinfo + count + 1);
+ i = 0;
+
+ n = 0;
+ offset = pos = iter->pos;
+
+ while (offset < iter->next) {
+ offset = get_string(iter, pos, &line, &linesz);
+ if (offset <= 0) {
+ count = (offset) ? -errno : -EINVAL;
+ free(*modinfo);
+ goto fail;
+ }
+
+ strcpy(s + i, line + modlen);
+ (*modinfo)[n++] = s + i;
+ i += linesz - modlen;
+
+ pos = offset;
+ }
+fail:
+ kmod_builtin_iter_free(iter);
+ return count;
+}
diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c
new file mode 100644
index 0000000..e83621b
--- /dev/null
+++ b/libkmod/libkmod-config.c
@@ -0,0 +1,1252 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <shared/util.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+struct kmod_alias {
+ char *name;
+ char modname[];
+};
+
+struct kmod_options {
+ char *options;
+ char modname[];
+};
+
+struct kmod_command {
+ char *command;
+ char modname[];
+};
+
+struct kmod_softdep {
+ char *name;
+ const char **pre;
+ const char **post;
+ unsigned int n_pre;
+ unsigned int n_post;
+};
+
+const char *kmod_blacklist_get_modname(const struct kmod_list *l)
+{
+ return l->data;
+}
+
+const char *kmod_alias_get_name(const struct kmod_list *l) {
+ const struct kmod_alias *alias = l->data;
+ return alias->name;
+}
+
+const char *kmod_alias_get_modname(const struct kmod_list *l) {
+ const struct kmod_alias *alias = l->data;
+ return alias->modname;
+}
+
+const char *kmod_option_get_options(const struct kmod_list *l) {
+ const struct kmod_options *alias = l->data;
+ return alias->options;
+}
+
+const char *kmod_option_get_modname(const struct kmod_list *l) {
+ const struct kmod_options *alias = l->data;
+ return alias->modname;
+}
+
+const char *kmod_command_get_command(const struct kmod_list *l) {
+ const struct kmod_command *alias = l->data;
+ return alias->command;
+}
+
+const char *kmod_command_get_modname(const struct kmod_list *l) {
+ const struct kmod_command *alias = l->data;
+ return alias->modname;
+}
+
+const char *kmod_softdep_get_name(const struct kmod_list *l) {
+ const struct kmod_softdep *dep = l->data;
+ return dep->name;
+}
+
+const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) {
+ const struct kmod_softdep *dep = l->data;
+ *count = dep->n_pre;
+ return dep->pre;
+}
+
+const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count) {
+ const struct kmod_softdep *dep = l->data;
+ *count = dep->n_post;
+ return dep->post;
+}
+
+static int kmod_config_add_command(struct kmod_config *config,
+ const char *modname,
+ const char *command,
+ const char *command_name,
+ struct kmod_list **list)
+{
+ _cleanup_free_ struct kmod_command *cmd;
+ struct kmod_list *l;
+ size_t modnamelen = strlen(modname) + 1;
+ size_t commandlen = strlen(command) + 1;
+
+ DBG(config->ctx, "modname='%s' cmd='%s %s'\n", modname, command_name,
+ command);
+
+ cmd = malloc(sizeof(*cmd) + modnamelen + commandlen);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->command = sizeof(*cmd) + modnamelen + (char *)cmd;
+ memcpy(cmd->modname, modname, modnamelen);
+ memcpy(cmd->command, command, commandlen);
+
+ l = kmod_list_append(*list, cmd);
+ if (!l)
+ return -ENOMEM;
+
+ *list = l;
+ cmd = NULL;
+ return 0;
+}
+
+static void kmod_config_free_command(struct kmod_config *config,
+ struct kmod_list *l,
+ struct kmod_list **list)
+{
+ struct kmod_command *cmd = l->data;
+
+ free(cmd);
+ *list = kmod_list_remove(l);
+}
+
+static int kmod_config_add_options(struct kmod_config *config,
+ const char *modname, const char *options)
+{
+ _cleanup_free_ struct kmod_options *opt;
+ struct kmod_list *list;
+ size_t modnamelen = strlen(modname) + 1;
+ size_t optionslen = strlen(options) + 1;
+
+ DBG(config->ctx, "modname='%s' options='%s'\n", modname, options);
+
+ opt = malloc(sizeof(*opt) + modnamelen + optionslen);
+ if (!opt)
+ return -ENOMEM;
+
+ opt->options = sizeof(*opt) + modnamelen + (char *)opt;
+
+ memcpy(opt->modname, modname, modnamelen);
+ memcpy(opt->options, options, optionslen);
+ strchr_replace(opt->options, '\t', ' ');
+
+ list = kmod_list_append(config->options, opt);
+ if (!list)
+ return -ENOMEM;
+
+ opt = NULL;
+ config->options = list;
+ return 0;
+}
+
+static void kmod_config_free_options(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ struct kmod_options *opt = l->data;
+
+ free(opt);
+
+ config->options = kmod_list_remove(l);
+}
+
+static int kmod_config_add_alias(struct kmod_config *config,
+ const char *name, const char *modname)
+{
+ _cleanup_free_ struct kmod_alias *alias;
+ struct kmod_list *list;
+ size_t namelen = strlen(name) + 1, modnamelen = strlen(modname) + 1;
+
+ DBG(config->ctx, "name=%s modname=%s\n", name, modname);
+
+ alias = malloc(sizeof(*alias) + namelen + modnamelen);
+ if (!alias)
+ return -ENOMEM;
+
+ alias->name = sizeof(*alias) + modnamelen + (char *)alias;
+
+ memcpy(alias->modname, modname, modnamelen);
+ memcpy(alias->name, name, namelen);
+
+ list = kmod_list_append(config->aliases, alias);
+ if (!list)
+ return -ENOMEM;
+
+ alias = NULL;
+ config->aliases = list;
+ return 0;
+}
+
+static void kmod_config_free_alias(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ struct kmod_alias *alias = l->data;
+
+ free(alias);
+
+ config->aliases = kmod_list_remove(l);
+}
+
+static int kmod_config_add_blacklist(struct kmod_config *config,
+ const char *modname)
+{
+ _cleanup_free_ char *p;
+ struct kmod_list *list;
+
+ DBG(config->ctx, "modname=%s\n", modname);
+
+ p = strdup(modname);
+ if (!p)
+ return -ENOMEM;
+
+ list = kmod_list_append(config->blacklists, p);
+ if (!list)
+ return -ENOMEM;
+
+ p = NULL;
+ config->blacklists = list;
+ return 0;
+}
+
+static void kmod_config_free_blacklist(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ free(l->data);
+ config->blacklists = kmod_list_remove(l);
+}
+
+static int kmod_config_add_softdep(struct kmod_config *config,
+ const char *modname,
+ const char *line)
+{
+ struct kmod_list *list;
+ struct kmod_softdep *dep;
+ const char *s, *p;
+ char *itr;
+ unsigned int n_pre = 0, n_post = 0;
+ size_t modnamelen = strlen(modname) + 1;
+ size_t buflen = 0;
+ bool was_space = false;
+ enum { S_NONE, S_PRE, S_POST } mode = S_NONE;
+
+ DBG(config->ctx, "modname=%s\n", modname);
+
+ /* analyze and count */
+ for (p = s = line; ; s++) {
+ size_t plen;
+
+ if (*s != '\0') {
+ if (!isspace(*s)) {
+ was_space = false;
+ continue;
+ }
+
+ if (was_space) {
+ p = s + 1;
+ continue;
+ }
+ was_space = true;
+
+ if (p >= s)
+ continue;
+ }
+ plen = s - p;
+
+ if (plen == sizeof("pre:") - 1 &&
+ memcmp(p, "pre:", sizeof("pre:") - 1) == 0)
+ mode = S_PRE;
+ else if (plen == sizeof("post:") - 1 &&
+ memcmp(p, "post:", sizeof("post:") - 1) == 0)
+ mode = S_POST;
+ else if (*s != '\0' || (*s == '\0' && !was_space)) {
+ if (mode == S_PRE) {
+ buflen += plen + 1;
+ n_pre++;
+ } else if (mode == S_POST) {
+ buflen += plen + 1;
+ n_post++;
+ }
+ }
+ p = s + 1;
+ if (*s == '\0')
+ break;
+ }
+
+ DBG(config->ctx, "%u pre, %u post\n", n_pre, n_post);
+
+ dep = malloc(sizeof(struct kmod_softdep) + modnamelen +
+ n_pre * sizeof(const char *) +
+ n_post * sizeof(const char *) +
+ buflen);
+ if (dep == NULL) {
+ ERR(config->ctx, "out-of-memory modname=%s\n", modname);
+ return -ENOMEM;
+ }
+ dep->n_pre = n_pre;
+ dep->n_post = n_post;
+ dep->pre = (const char **)((char *)dep + sizeof(struct kmod_softdep));
+ dep->post = dep->pre + n_pre;
+ dep->name = (char *)(dep->post + n_post);
+
+ memcpy(dep->name, modname, modnamelen);
+
+ /* copy strings */
+ itr = dep->name + modnamelen;
+ n_pre = 0;
+ n_post = 0;
+ mode = S_NONE;
+ was_space = false;
+ for (p = s = line; ; s++) {
+ size_t plen;
+
+ if (*s != '\0') {
+ if (!isspace(*s)) {
+ was_space = false;
+ continue;
+ }
+
+ if (was_space) {
+ p = s + 1;
+ continue;
+ }
+ was_space = true;
+
+ if (p >= s)
+ continue;
+ }
+ plen = s - p;
+
+ if (plen == sizeof("pre:") - 1 &&
+ memcmp(p, "pre:", sizeof("pre:") - 1) == 0)
+ mode = S_PRE;
+ else if (plen == sizeof("post:") - 1 &&
+ memcmp(p, "post:", sizeof("post:") - 1) == 0)
+ mode = S_POST;
+ else if (*s != '\0' || (*s == '\0' && !was_space)) {
+ if (mode == S_PRE) {
+ dep->pre[n_pre] = itr;
+ memcpy(itr, p, plen);
+ itr[plen] = '\0';
+ itr += plen + 1;
+ n_pre++;
+ } else if (mode == S_POST) {
+ dep->post[n_post] = itr;
+ memcpy(itr, p, plen);
+ itr[plen] = '\0';
+ itr += plen + 1;
+ n_post++;
+ }
+ }
+ p = s + 1;
+ if (*s == '\0')
+ break;
+ }
+
+ list = kmod_list_append(config->softdeps, dep);
+ if (list == NULL) {
+ free(dep);
+ return -ENOMEM;
+ }
+ config->softdeps = list;
+
+ return 0;
+}
+
+static char *softdep_to_char(struct kmod_softdep *dep) {
+ const size_t sz_preprefix = sizeof("pre: ") - 1;
+ const size_t sz_postprefix = sizeof("post: ") - 1;
+ size_t sz = 1; /* at least '\0' */
+ size_t sz_pre, sz_post;
+ const char *start, *end;
+ char *s, *itr;
+
+ /*
+ * Rely on the fact that dep->pre[] and dep->post[] are strv's that
+ * point to a contiguous buffer
+ */
+ if (dep->n_pre > 0) {
+ start = dep->pre[0];
+ end = dep->pre[dep->n_pre - 1]
+ + strlen(dep->pre[dep->n_pre - 1]);
+ sz_pre = end - start;
+ sz += sz_pre + sz_preprefix;
+ } else
+ sz_pre = 0;
+
+ if (dep->n_post > 0) {
+ start = dep->post[0];
+ end = dep->post[dep->n_post - 1]
+ + strlen(dep->post[dep->n_post - 1]);
+ sz_post = end - start;
+ sz += sz_post + sz_postprefix;
+ } else
+ sz_post = 0;
+
+ itr = s = malloc(sz);
+ if (s == NULL)
+ return NULL;
+
+ if (sz_pre) {
+ char *p;
+
+ memcpy(itr, "pre: ", sz_preprefix);
+ itr += sz_preprefix;
+
+ /* include last '\0' */
+ memcpy(itr, dep->pre[0], sz_pre + 1);
+ for (p = itr; p < itr + sz_pre; p++) {
+ if (*p == '\0')
+ *p = ' ';
+ }
+ itr = p;
+ }
+
+ if (sz_post) {
+ char *p;
+
+ memcpy(itr, "post: ", sz_postprefix);
+ itr += sz_postprefix;
+
+ /* include last '\0' */
+ memcpy(itr, dep->post[0], sz_post + 1);
+ for (p = itr; p < itr + sz_post; p++) {
+ if (*p == '\0')
+ *p = ' ';
+ }
+ itr = p;
+ }
+
+ *itr = '\0';
+
+ return s;
+}
+
+static void kmod_config_free_softdep(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ free(l->data);
+ config->softdeps = kmod_list_remove(l);
+}
+
+static void kcmdline_parse_result(struct kmod_config *config, char *modname,
+ char *param, char *value)
+{
+ if (modname == NULL || param == NULL)
+ return;
+
+ DBG(config->ctx, "%s %s\n", modname, param);
+
+ if (streq(modname, "modprobe") && !strncmp(param, "blacklist=", 10)) {
+ for (;;) {
+ char *t = strsep(&value, ",");
+ if (t == NULL)
+ break;
+
+ kmod_config_add_blacklist(config, t);
+ }
+ } else {
+ if (underscores(modname) < 0) {
+ ERR(config->ctx, "Ignoring bad option on kernel command line while parsing module name: '%s'\n",
+ modname);
+ } else {
+ kmod_config_add_options(config, modname, param);
+ }
+ }
+}
+
+static int kmod_config_parse_kcmdline(struct kmod_config *config)
+{
+ char buf[KCMD_LINE_SIZE];
+ int fd, err;
+ char *p, *p_quote_start, *modname, *param = NULL, *value = NULL;
+ bool is_quoted = false, iter = true;
+ enum state {
+ STATE_IGNORE,
+ STATE_MODNAME,
+ STATE_PARAM,
+ STATE_VALUE,
+ STATE_COMPLETE,
+ } state;
+
+ fd = open("/proc/cmdline", O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ err = -errno;
+ DBG(config->ctx, "could not open '/proc/cmdline' for reading: %m\n");
+ return err;
+ }
+
+ err = read_str_safe(fd, buf, sizeof(buf));
+ close(fd);
+ if (err < 0) {
+ ERR(config->ctx, "could not read from '/proc/cmdline': %s\n",
+ strerror(-err));
+ return err;
+ }
+
+ state = STATE_MODNAME;
+ p_quote_start = NULL;
+ for (p = buf, modname = buf; iter; p++) {
+ switch (*p) {
+ case '"':
+ is_quoted = !is_quoted;
+
+ /*
+ * only allow starting quote as first char when looking
+ * for a modname: anything else is considered ill-formed
+ */
+ if (is_quoted && state == STATE_MODNAME && p == modname) {
+ p_quote_start = p;
+ modname = p + 1;
+ } else if (state != STATE_VALUE) {
+ state = STATE_IGNORE;
+ }
+
+ break;
+ case '\0':
+ iter = false;
+ /* fall-through */
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\v':
+ case '\f':
+ case '\r':
+ if (is_quoted && state == STATE_VALUE) {
+ /* no state change*/;
+ } else if (is_quoted) {
+ /* spaces are only allowed in the value part */
+ state = STATE_IGNORE;
+ } else if (state == STATE_VALUE || state == STATE_PARAM) {
+ *p = '\0';
+ state = STATE_COMPLETE;
+ } else {
+ /*
+ * go to next option, ignoring any possible
+ * partial match we have
+ */
+ modname = p + 1;
+ state = STATE_MODNAME;
+ p_quote_start = NULL;
+ }
+ break;
+ case '.':
+ if (state == STATE_MODNAME) {
+ *p = '\0';
+ param = p + 1;
+ state = STATE_PARAM;
+ } else if (state == STATE_PARAM) {
+ state = STATE_IGNORE;
+ }
+ break;
+ case '=':
+ if (state == STATE_PARAM) {
+ /*
+ * Don't set *p to '\0': the value var shadows
+ * param
+ */
+ value = p + 1;
+ state = STATE_VALUE;
+ } else if (state == STATE_MODNAME) {
+ state = STATE_IGNORE;
+ }
+ break;
+ }
+
+ if (state == STATE_COMPLETE) {
+ /*
+ * We may need to re-quote to unmangle what the
+ * bootloader passed. Example: grub passes the option as
+ * "parport.dyndbg=file drivers/parport/ieee1284_ops.c +mpf"
+ * instead of
+ * parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf"
+ */
+ if (p_quote_start && p_quote_start < modname) {
+ /*
+ * p_quote_start
+ * |
+ * |modname param value
+ * || | |
+ * vv v v
+ * "parport\0dyndbg=file drivers/parport/ieee1284_ops.c +mpf" */
+ memmove(p_quote_start, modname, value - modname);
+ value--; modname--; param--;
+ *value = '"';
+ }
+ kcmdline_parse_result(config, modname, param, value);
+ /* start over on next iteration */
+ modname = p + 1;
+ state = STATE_MODNAME;
+ p_quote_start = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Take an fd and own it. It will be closed on return. filename is used only
+ * for debug messages
+ */
+static int kmod_config_parse(struct kmod_config *config, int fd,
+ const char *filename)
+{
+ struct kmod_ctx *ctx = config->ctx;
+ char *line;
+ FILE *fp;
+ unsigned int linenum = 0;
+ int err;
+
+ fp = fdopen(fd, "r");
+ if (fp == NULL) {
+ err = -errno;
+ ERR(config->ctx, "fd %d: %m\n", fd);
+ close(fd);
+ return err;
+ }
+
+ while ((line = freadline_wrapped(fp, &linenum)) != NULL) {
+ char *cmd, *saveptr;
+
+ if (line[0] == '\0' || line[0] == '#')
+ goto done_next;
+
+ cmd = strtok_r(line, "\t ", &saveptr);
+ if (cmd == NULL)
+ goto done_next;
+
+ if (streq(cmd, "alias")) {
+ char *alias = strtok_r(NULL, "\t ", &saveptr);
+ char *modname = strtok_r(NULL, "\t ", &saveptr);
+
+ if (underscores(alias) < 0 || underscores(modname) < 0)
+ goto syntax_error;
+
+ kmod_config_add_alias(config, alias, modname);
+ } else if (streq(cmd, "blacklist")) {
+ char *modname = strtok_r(NULL, "\t ", &saveptr);
+
+ if (underscores(modname) < 0)
+ goto syntax_error;
+
+ kmod_config_add_blacklist(config, modname);
+ } else if (streq(cmd, "options")) {
+ char *modname = strtok_r(NULL, "\t ", &saveptr);
+ char *options = strtok_r(NULL, "\0", &saveptr);
+
+ if (underscores(modname) < 0 || options == NULL)
+ goto syntax_error;
+
+ kmod_config_add_options(config, modname, options);
+ } else if (streq(cmd, "install")) {
+ char *modname = strtok_r(NULL, "\t ", &saveptr);
+ char *installcmd = strtok_r(NULL, "\0", &saveptr);
+
+ if (underscores(modname) < 0 || installcmd == NULL)
+ goto syntax_error;
+
+ kmod_config_add_command(config, modname, installcmd,
+ cmd, &config->install_commands);
+ } else if (streq(cmd, "remove")) {
+ char *modname = strtok_r(NULL, "\t ", &saveptr);
+ char *removecmd = strtok_r(NULL, "\0", &saveptr);
+
+ if (underscores(modname) < 0 || removecmd == NULL)
+ goto syntax_error;
+
+ kmod_config_add_command(config, modname, removecmd,
+ cmd, &config->remove_commands);
+ } else if (streq(cmd, "softdep")) {
+ char *modname = strtok_r(NULL, "\t ", &saveptr);
+ char *softdeps = strtok_r(NULL, "\0", &saveptr);
+
+ if (underscores(modname) < 0 || softdeps == NULL)
+ goto syntax_error;
+
+ kmod_config_add_softdep(config, modname, softdeps);
+ } else if (streq(cmd, "include")
+ || streq(cmd, "config")) {
+ ERR(ctx, "%s: command %s is deprecated and not parsed anymore\n",
+ filename, cmd);
+ } else {
+syntax_error:
+ ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n",
+ filename, linenum, cmd);
+ }
+
+done_next:
+ free(line);
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+void kmod_config_free(struct kmod_config *config)
+{
+ while (config->aliases)
+ kmod_config_free_alias(config, config->aliases);
+
+ while (config->blacklists)
+ kmod_config_free_blacklist(config, config->blacklists);
+
+ while (config->options)
+ kmod_config_free_options(config, config->options);
+
+ while (config->install_commands) {
+ kmod_config_free_command(config, config->install_commands,
+ &config->install_commands);
+ }
+
+ while (config->remove_commands) {
+ kmod_config_free_command(config, config->remove_commands,
+ &config->remove_commands);
+ }
+
+ while (config->softdeps)
+ kmod_config_free_softdep(config, config->softdeps);
+
+ for (; config->paths != NULL;
+ config->paths = kmod_list_remove(config->paths))
+ free(config->paths->data);
+
+ free(config);
+}
+
+static bool conf_files_filter_out(struct kmod_ctx *ctx, DIR *d,
+ const char *path, const char *fn)
+{
+ size_t len = strlen(fn);
+ struct stat st;
+
+ if (fn[0] == '.')
+ return true;
+
+ if (len < 6 || (!streq(&fn[len - 5], ".conf")
+ && !streq(&fn[len - 6], ".alias")))
+ return true;
+
+ fstatat(dirfd(d), fn, &st, 0);
+
+ if (S_ISDIR(st.st_mode)) {
+ ERR(ctx, "Directories inside directories are not supported: "
+ "%s/%s\n", path, fn);
+ return true;
+ }
+
+ return false;
+}
+
+struct conf_file {
+ const char *path;
+ bool is_single;
+ char name[];
+};
+
+static int conf_files_insert_sorted(struct kmod_ctx *ctx,
+ struct kmod_list **list,
+ const char *path, const char *name)
+{
+ struct kmod_list *lpos, *tmp;
+ struct conf_file *cf;
+ size_t namelen;
+ int cmp = -1;
+ bool is_single = false;
+
+ if (name == NULL) {
+ name = basename(path);
+ is_single = true;
+ }
+
+ kmod_list_foreach(lpos, *list) {
+ cf = lpos->data;
+
+ if ((cmp = strcmp(name, cf->name)) <= 0)
+ break;
+ }
+
+ if (cmp == 0) {
+ DBG(ctx, "Ignoring duplicate config file: %s/%s\n", path,
+ name);
+ return -EEXIST;
+ }
+
+ namelen = strlen(name);
+ cf = malloc(sizeof(*cf) + namelen + 1);
+ if (cf == NULL)
+ return -ENOMEM;
+
+ memcpy(cf->name, name, namelen + 1);
+ cf->path = path;
+ cf->is_single = is_single;
+
+ if (lpos == NULL)
+ tmp = kmod_list_append(*list, cf);
+ else if (lpos == *list)
+ tmp = kmod_list_prepend(*list, cf);
+ else
+ tmp = kmod_list_insert_before(lpos, cf);
+
+ if (tmp == NULL) {
+ free(cf);
+ return -ENOMEM;
+ }
+
+ if (lpos == NULL || lpos == *list)
+ *list = tmp;
+
+ return 0;
+}
+
+/*
+ * Insert configuration files in @list, ignoring duplicates
+ */
+static int conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list,
+ const char *path,
+ unsigned long long *path_stamp)
+{
+ DIR *d;
+ int err;
+ struct stat st;
+ struct dirent *dent;
+
+ if (stat(path, &st) != 0) {
+ err = -errno;
+ DBG(ctx, "could not stat '%s': %m\n", path);
+ return err;
+ }
+
+ *path_stamp = stat_mstamp(&st);
+
+ if (!S_ISDIR(st.st_mode)) {
+ conf_files_insert_sorted(ctx, list, path, NULL);
+ return 0;
+ }
+
+ d = opendir(path);
+ if (d == NULL) {
+ ERR(ctx, "opendir(%s): %m\n", path);
+ return -EINVAL;
+ }
+
+ for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
+ if (conf_files_filter_out(ctx, d, path, dent->d_name))
+ continue;
+
+ conf_files_insert_sorted(ctx, list, path, dent->d_name);
+ }
+
+ closedir(d);
+ return 0;
+}
+
+int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
+ const char * const *config_paths)
+{
+ struct kmod_config *config;
+ struct kmod_list *list = NULL;
+ struct kmod_list *path_list = NULL;
+ size_t i;
+
+ conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.softdep");
+
+ for (i = 0; config_paths[i] != NULL; i++) {
+ const char *path = config_paths[i];
+ unsigned long long path_stamp = 0;
+ size_t pathlen;
+ struct kmod_list *tmp;
+ struct kmod_config_path *cf;
+
+ if (conf_files_list(ctx, &list, path, &path_stamp) < 0)
+ continue;
+
+ pathlen = strlen(path) + 1;
+ cf = malloc(sizeof(*cf) + pathlen);
+ if (cf == NULL)
+ goto oom;
+
+ cf->stamp = path_stamp;
+ memcpy(cf->path, path, pathlen);
+
+ tmp = kmod_list_append(path_list, cf);
+ if (tmp == NULL) {
+ free(cf);
+ goto oom;
+ }
+ path_list = tmp;
+ }
+
+ *p_config = config = calloc(1, sizeof(struct kmod_config));
+ if (config == NULL)
+ goto oom;
+
+ config->paths = path_list;
+ config->ctx = ctx;
+
+ for (; list != NULL; list = kmod_list_remove(list)) {
+ char buf[PATH_MAX];
+ const char *fn = buf;
+ struct conf_file *cf = list->data;
+ int fd;
+
+ if (cf->is_single) {
+ fn = cf->path;
+ } else if (snprintf(buf, sizeof(buf), "%s/%s",
+ cf->path, cf->name) >= (int)sizeof(buf)) {
+ ERR(ctx, "Error parsing %s/%s: path too long\n",
+ cf->path, cf->name);
+ free(cf);
+ continue;
+ }
+
+ fd = open(fn, O_RDONLY|O_CLOEXEC);
+ DBG(ctx, "parsing file '%s' fd=%d\n", fn, fd);
+
+ if (fd >= 0)
+ kmod_config_parse(config, fd, fn);
+
+ free(cf);
+ }
+
+ kmod_config_parse_kcmdline(config);
+
+ return 0;
+
+oom:
+ for (; list != NULL; list = kmod_list_remove(list))
+ free(list->data);
+
+ for (; path_list != NULL; path_list = kmod_list_remove(path_list))
+ free(path_list->data);
+
+ return -ENOMEM;
+}
+
+/**********************************************************************
+ * struct kmod_config_iter functions
+ **********************************************************************/
+
+enum config_type {
+ CONFIG_TYPE_BLACKLIST = 0,
+ CONFIG_TYPE_INSTALL,
+ CONFIG_TYPE_REMOVE,
+ CONFIG_TYPE_ALIAS,
+ CONFIG_TYPE_OPTION,
+ CONFIG_TYPE_SOFTDEP,
+};
+
+struct kmod_config_iter {
+ enum config_type type;
+ bool intermediate;
+ const struct kmod_list *list;
+ const struct kmod_list *curr;
+ void *data;
+ const char *(*get_key)(const struct kmod_list *l);
+ const char *(*get_value)(const struct kmod_list *l);
+};
+
+static const char *softdep_get_plain_softdep(const struct kmod_list *l)
+{
+ char *s = softdep_to_char(l->data);
+ return s;
+}
+
+static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx* ctx,
+ enum config_type type)
+{
+ struct kmod_config_iter *iter = calloc(1, sizeof(*iter));
+ const struct kmod_config *config = kmod_get_config(ctx);
+
+ if (iter == NULL)
+ return NULL;
+
+ iter->type = type;
+
+ switch (type) {
+ case CONFIG_TYPE_BLACKLIST:
+ iter->list = config->blacklists;
+ iter->get_key = kmod_blacklist_get_modname;
+ break;
+ case CONFIG_TYPE_INSTALL:
+ iter->list = config->install_commands;
+ iter->get_key = kmod_command_get_modname;
+ iter->get_value = kmod_command_get_command;
+ break;
+ case CONFIG_TYPE_REMOVE:
+ iter->list = config->remove_commands;
+ iter->get_key = kmod_command_get_modname;
+ iter->get_value = kmod_command_get_command;
+ break;
+ case CONFIG_TYPE_ALIAS:
+ iter->list = config->aliases;
+ iter->get_key = kmod_alias_get_name;
+ iter->get_value = kmod_alias_get_modname;
+ break;
+ case CONFIG_TYPE_OPTION:
+ iter->list = config->options;
+ iter->get_key = kmod_option_get_modname;
+ iter->get_value = kmod_option_get_options;
+ break;
+ case CONFIG_TYPE_SOFTDEP:
+ iter->list = config->softdeps;
+ iter->get_key = kmod_softdep_get_name;
+ iter->get_value = softdep_get_plain_softdep;
+ iter->intermediate = true;
+ break;
+ }
+
+ return iter;
+}
+
+/**
+ * SECTION:libkmod-config
+ * @short_description: retrieve current libkmod configuration
+ */
+
+/**
+ * kmod_config_get_blacklists:
+ * @ctx: kmod library context
+ *
+ * Retrieve an iterator to deal with the blacklist maintained inside the
+ * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
+ * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
+ * be made to initialize the iterator and check if it's valid.
+ *
+ * Returns: a new iterator over the blacklists or NULL on failure. Free it
+ * with kmod_config_iter_free_iter().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_blacklists(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;;
+
+ return kmod_config_iter_new(ctx, CONFIG_TYPE_BLACKLIST);
+}
+
+/**
+ * kmod_config_get_install_commands:
+ * @ctx: kmod library context
+ *
+ * Retrieve an iterator to deal with the install commands maintained inside the
+ * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
+ * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
+ * be made to initialize the iterator and check if it's valid.
+ *
+ * Returns: a new iterator over the install commands or NULL on failure. Free
+ * it with kmod_config_iter_free_iter().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_install_commands(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;;
+
+ return kmod_config_iter_new(ctx, CONFIG_TYPE_INSTALL);
+}
+
+/**
+ * kmod_config_get_remove_commands:
+ * @ctx: kmod library context
+ *
+ * Retrieve an iterator to deal with the remove commands maintained inside the
+ * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
+ * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
+ * be made to initialize the iterator and check if it's valid.
+ *
+ * Returns: a new iterator over the remove commands or NULL on failure. Free
+ * it with kmod_config_iter_free_iter().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;;
+
+ return kmod_config_iter_new(ctx, CONFIG_TYPE_REMOVE);
+}
+
+/**
+ * kmod_config_get_aliases:
+ * @ctx: kmod library context
+ *
+ * Retrieve an iterator to deal with the aliases maintained inside the
+ * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
+ * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
+ * be made to initialize the iterator and check if it's valid.
+ *
+ * Returns: a new iterator over the aliases or NULL on failure. Free it with
+ * kmod_config_iter_free_iter().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;;
+
+ return kmod_config_iter_new(ctx, CONFIG_TYPE_ALIAS);
+}
+
+/**
+ * kmod_config_get_options:
+ * @ctx: kmod library context
+ *
+ * Retrieve an iterator to deal with the options maintained inside the
+ * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
+ * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
+ * be made to initialize the iterator and check if it's valid.
+ *
+ * Returns: a new iterator over the options or NULL on failure. Free it with
+ * kmod_config_iter_free_iter().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_options(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;;
+
+ return kmod_config_iter_new(ctx, CONFIG_TYPE_OPTION);
+}
+
+/**
+ * kmod_config_get_softdeps:
+ * @ctx: kmod library context
+ *
+ * Retrieve an iterator to deal with the softdeps maintained inside the
+ * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
+ * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
+ * be made to initialize the iterator and check if it's valid.
+ *
+ * Returns: a new iterator over the softdeps or NULL on failure. Free it with
+ * kmod_config_iter_free_iter().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;;
+
+ return kmod_config_iter_new(ctx, CONFIG_TYPE_SOFTDEP);
+}
+
+/**
+ * kmod_config_iter_get_key:
+ * @iter: iterator over a certain configuration
+ *
+ * When using a new allocated iterator, user must perform a call to
+ * kmod_config_iter_next() to initialize iterator's position and check if it's
+ * valid.
+ *
+ * Returns: the key of the current configuration pointed by @iter.
+ */
+KMOD_EXPORT const char *kmod_config_iter_get_key(const struct kmod_config_iter *iter)
+{
+ if (iter == NULL || iter->curr == NULL)
+ return NULL;
+
+ return iter->get_key(iter->curr);
+}
+
+/**
+ * kmod_config_iter_get_value:
+ * @iter: iterator over a certain configuration
+ *
+ * When using a new allocated iterator, user must perform a call to
+ * kmod_config_iter_next() to initialize iterator's position and check if it's
+ * valid.
+ *
+ * Returns: the value of the current configuration pointed by @iter.
+ */
+KMOD_EXPORT const char *kmod_config_iter_get_value(const struct kmod_config_iter *iter)
+{
+ const char *s;
+
+ if (iter == NULL || iter->curr == NULL)
+ return NULL;
+
+ if (iter->get_value == NULL)
+ return NULL;
+
+ if (iter->intermediate) {
+ struct kmod_config_iter *i = (struct kmod_config_iter *)iter;
+
+ free(i->data);
+ s = i->data = (void *) iter->get_value(iter->curr);
+ } else
+ s = iter->get_value(iter->curr);
+
+ return s;
+}
+
+/**
+ * kmod_config_iter_next:
+ * @iter: iterator over a certain configuration
+ *
+ * Make @iter point to the next item of a certain configuration. It's an
+ * automatically recycling iterator. When it reaches the end, false is
+ * returned; then if user wants to iterate again, it's sufficient to call this
+ * function once more.
+ *
+ * Returns: true if next position of @iter is valid or false if its end is
+ * reached.
+ */
+KMOD_EXPORT bool kmod_config_iter_next(struct kmod_config_iter *iter)
+{
+ if (iter == NULL)
+ return false;
+
+ if (iter->curr == NULL) {
+ iter->curr = iter->list;
+ return iter->curr != NULL;
+ }
+
+ iter->curr = kmod_list_next(iter->list, iter->curr);
+
+ return iter->curr != NULL;
+}
+
+/**
+ * kmod_config_iter_free_iter:
+ * @iter: iterator over a certain configuration
+ *
+ * Free resources used by the iterator.
+ */
+KMOD_EXPORT void kmod_config_iter_free_iter(struct kmod_config_iter *iter)
+{
+ free(iter->data);
+ free(iter);
+}
diff --git a/libkmod/libkmod-elf.c b/libkmod/libkmod-elf.c
new file mode 100644
index 0000000..933825b
--- /dev/null
+++ b/libkmod/libkmod-elf.c
@@ -0,0 +1,1219 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <elf.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/util.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+enum kmod_elf_class {
+ KMOD_ELF_32 = (1 << 1),
+ KMOD_ELF_64 = (1 << 2),
+ KMOD_ELF_LSB = (1 << 3),
+ KMOD_ELF_MSB = (1 << 4)
+};
+
+/* as defined in module-init-tools */
+struct kmod_modversion32 {
+ uint32_t crc;
+ char name[64 - sizeof(uint32_t)];
+};
+
+struct kmod_modversion64 {
+ uint64_t crc;
+ char name[64 - sizeof(uint64_t)];
+};
+
+struct kmod_elf {
+ const uint8_t *memory;
+ uint8_t *changed;
+ uint64_t size;
+ enum kmod_elf_class class;
+ struct kmod_elf_header {
+ struct {
+ uint64_t offset;
+ uint16_t count;
+ uint16_t entry_size;
+ } section;
+ struct {
+ uint16_t section; /* index of the strings section */
+ uint64_t size;
+ uint64_t offset;
+ uint32_t nameoff; /* offset in strings itself */
+ } strings;
+ uint16_t machine;
+ } header;
+};
+
+//#define ENABLE_ELFDBG 1
+
+#if defined(ENABLE_LOGGING) && defined(ENABLE_ELFDBG)
+#define ELFDBG(elf, ...) \
+ _elf_dbg(elf, __FILE__, __LINE__, __func__, __VA_ARGS__);
+
+static inline void _elf_dbg(const struct kmod_elf *elf, const char *fname, unsigned line, const char *func, const char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "ELFDBG-%d%c: %s:%u %s() ",
+ (elf->class & KMOD_ELF_32) ? 32 : 64,
+ (elf->class & KMOD_ELF_MSB) ? 'M' : 'L',
+ fname, line, func);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
+#else
+#define ELFDBG(elf, ...)
+#endif
+
+
+static int elf_identify(const void *memory, uint64_t size)
+{
+ const uint8_t *p = memory;
+ int class = 0;
+
+ if (size <= EI_NIDENT || memcmp(p, ELFMAG, SELFMAG) != 0)
+ return -ENOEXEC;
+
+ switch (p[EI_CLASS]) {
+ case ELFCLASS32:
+ if (size <= sizeof(Elf32_Ehdr))
+ return -EINVAL;
+ class |= KMOD_ELF_32;
+ break;
+ case ELFCLASS64:
+ if (size <= sizeof(Elf64_Ehdr))
+ return -EINVAL;
+ class |= KMOD_ELF_64;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (p[EI_DATA]) {
+ case ELFDATA2LSB:
+ class |= KMOD_ELF_LSB;
+ break;
+ case ELFDATA2MSB:
+ class |= KMOD_ELF_MSB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return class;
+}
+
+static inline uint64_t elf_get_uint(const struct kmod_elf *elf, uint64_t offset, uint16_t size)
+{
+ const uint8_t *p;
+ uint64_t ret = 0;
+ size_t i;
+
+ assert(size <= sizeof(uint64_t));
+ assert(offset + size <= elf->size);
+ if (offset + size > elf->size) {
+ ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
+ offset, size, offset + size, elf->size);
+ return (uint64_t)-1;
+ }
+
+ p = elf->memory + offset;
+ if (elf->class & KMOD_ELF_MSB) {
+ for (i = 0; i < size; i++)
+ ret = (ret << 8) | p[i];
+ } else {
+ for (i = 1; i <= size; i++)
+ ret = (ret << 8) | p[size - i];
+ }
+
+ ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64"\n",
+ size, offset, ret);
+
+ return ret;
+}
+
+static inline int elf_set_uint(struct kmod_elf *elf, uint64_t offset, uint64_t size, uint64_t value)
+{
+ uint8_t *p;
+ size_t i;
+
+ ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64" write memory=%p\n",
+ size, offset, value, elf->changed);
+
+ assert(size <= sizeof(uint64_t));
+ assert(offset + size <= elf->size);
+ if (offset + size > elf->size) {
+ ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
+ offset, size, offset + size, elf->size);
+ return -1;
+ }
+
+ if (elf->changed == NULL) {
+ elf->changed = malloc(elf->size);
+ if (elf->changed == NULL)
+ return -errno;
+ memcpy(elf->changed, elf->memory, elf->size);
+ elf->memory = elf->changed;
+ ELFDBG(elf, "copied memory to allow writing.\n");
+ }
+
+ p = elf->changed + offset;
+ if (elf->class & KMOD_ELF_MSB) {
+ for (i = 1; i <= size; i++) {
+ p[size - i] = value & 0xff;
+ value = (value & 0xffffffffffffff00) >> 8;
+ }
+ } else {
+ for (i = 0; i < size; i++) {
+ p[i] = value & 0xff;
+ value = (value & 0xffffffffffffff00) >> 8;
+ }
+ }
+
+ return 0;
+}
+
+static inline const void *elf_get_mem(const struct kmod_elf *elf, uint64_t offset)
+{
+ assert(offset < elf->size);
+ if (offset >= elf->size) {
+ ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
+ offset, elf->size);
+ return NULL;
+ }
+ return elf->memory + offset;
+}
+
+static inline const void *elf_get_section_header(const struct kmod_elf *elf, uint16_t idx)
+{
+ assert(idx != SHN_UNDEF);
+ assert(idx < elf->header.section.count);
+ if (idx == SHN_UNDEF || idx >= elf->header.section.count) {
+ ELFDBG(elf, "invalid section number: %"PRIu16", last=%"PRIu16"\n",
+ idx, elf->header.section.count);
+ return NULL;
+ }
+ return elf_get_mem(elf, elf->header.section.offset +
+ (uint64_t)(idx * elf->header.section.entry_size));
+}
+
+static inline int elf_get_section_info(const struct kmod_elf *elf, uint16_t idx, uint64_t *offset, uint64_t *size, uint32_t *nameoff)
+{
+ const uint8_t *p = elf_get_section_header(elf, idx);
+ uint64_t min_size, off = p - elf->memory;
+
+ if (p == NULL) {
+ ELFDBG(elf, "no section at %"PRIu16"\n", idx);
+ *offset = 0;
+ *size = 0;
+ *nameoff = 0;
+ return -EINVAL;
+ }
+
+#define READV(field) \
+ elf_get_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field))
+
+ if (elf->class & KMOD_ELF_32) {
+ const Elf32_Shdr *hdr _unused_ = (const Elf32_Shdr *)p;
+ *size = READV(sh_size);
+ *offset = READV(sh_offset);
+ *nameoff = READV(sh_name);
+ } else {
+ const Elf64_Shdr *hdr _unused_ = (const Elf64_Shdr *)p;
+ *size = READV(sh_size);
+ *offset = READV(sh_offset);
+ *nameoff = READV(sh_name);
+ }
+#undef READV
+
+ if (addu64_overflow(*offset, *size, &min_size)
+ || min_size > elf->size) {
+ ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
+ min_size, elf->size);
+ return -EINVAL;
+ }
+
+ ELFDBG(elf, "section=%"PRIu16" is: offset=%"PRIu64" size=%"PRIu64" nameoff=%"PRIu32"\n",
+ idx, *offset, *size, *nameoff);
+
+ return 0;
+}
+
+static const char *elf_get_strings_section(const struct kmod_elf *elf, uint64_t *size)
+{
+ *size = elf->header.strings.size;
+ return elf_get_mem(elf, elf->header.strings.offset);
+}
+
+struct kmod_elf *kmod_elf_new(const void *memory, off_t size)
+{
+ struct kmod_elf *elf;
+ uint64_t min_size;
+ size_t shdrs_size, shdr_size;
+ int class;
+
+ assert_cc(sizeof(uint16_t) == sizeof(Elf32_Half));
+ assert_cc(sizeof(uint16_t) == sizeof(Elf64_Half));
+ assert_cc(sizeof(uint32_t) == sizeof(Elf32_Word));
+ assert_cc(sizeof(uint32_t) == sizeof(Elf64_Word));
+
+ if (!memory) {
+ errno = -EINVAL;
+ return NULL;
+ }
+
+ class = elf_identify(memory, size);
+ if (class < 0) {
+ errno = -class;
+ return NULL;
+ }
+
+ elf = malloc(sizeof(struct kmod_elf));
+ if (elf == NULL) {
+ return NULL;
+ }
+
+ elf->memory = memory;
+ elf->changed = NULL;
+ elf->size = size;
+ elf->class = class;
+
+#define READV(field) \
+ elf_get_uint(elf, offsetof(typeof(*hdr), field), sizeof(hdr->field))
+
+#define LOAD_HEADER \
+ elf->header.section.offset = READV(e_shoff); \
+ elf->header.section.count = READV(e_shnum); \
+ elf->header.section.entry_size = READV(e_shentsize); \
+ elf->header.strings.section = READV(e_shstrndx); \
+ elf->header.machine = READV(e_machine)
+ if (elf->class & KMOD_ELF_32) {
+ const Elf32_Ehdr *hdr _unused_ = elf_get_mem(elf, 0);
+ LOAD_HEADER;
+ shdr_size = sizeof(Elf32_Shdr);
+ } else {
+ const Elf64_Ehdr *hdr _unused_ = elf_get_mem(elf, 0);
+ LOAD_HEADER;
+ shdr_size = sizeof(Elf64_Shdr);
+ }
+#undef LOAD_HEADER
+#undef READV
+
+ ELFDBG(elf, "section: offset=%"PRIu64" count=%"PRIu16" entry_size=%"PRIu16" strings index=%"PRIu16"\n",
+ elf->header.section.offset,
+ elf->header.section.count,
+ elf->header.section.entry_size,
+ elf->header.strings.section);
+
+ if (elf->header.section.entry_size != shdr_size) {
+ ELFDBG(elf, "unexpected section entry size: %"PRIu16", expected %"PRIu16"\n",
+ elf->header.section.entry_size, shdr_size);
+ goto invalid;
+ }
+ shdrs_size = shdr_size * elf->header.section.count;
+ if (addu64_overflow(shdrs_size, elf->header.section.offset, &min_size)
+ || min_size > elf->size) {
+ ELFDBG(elf, "file is too short to hold sections\n");
+ goto invalid;
+ }
+
+ if (elf_get_section_info(elf, elf->header.strings.section,
+ &elf->header.strings.offset,
+ &elf->header.strings.size,
+ &elf->header.strings.nameoff) < 0) {
+ ELFDBG(elf, "could not get strings section\n");
+ goto invalid;
+ } else {
+ uint64_t slen;
+ const char *s = elf_get_strings_section(elf, &slen);
+ if (slen == 0 || s[slen - 1] != '\0') {
+ ELFDBG(elf, "strings section does not ends with \\0\n");
+ goto invalid;
+ }
+ }
+
+ return elf;
+
+invalid:
+ free(elf);
+ errno = EINVAL;
+ return NULL;
+}
+
+void kmod_elf_unref(struct kmod_elf *elf)
+{
+ free(elf->changed);
+ free(elf);
+}
+
+const void *kmod_elf_get_memory(const struct kmod_elf *elf)
+{
+ return elf->memory;
+}
+
+static int elf_find_section(const struct kmod_elf *elf, const char *section)
+{
+ uint64_t nameslen;
+ const char *names = elf_get_strings_section(elf, &nameslen);
+ uint16_t i;
+
+ for (i = 1; i < elf->header.section.count; i++) {
+ uint64_t off, size;
+ uint32_t nameoff;
+ const char *n;
+ int err = elf_get_section_info(elf, i, &off, &size, &nameoff);
+ if (err < 0)
+ continue;
+ if (nameoff >= nameslen)
+ continue;
+ n = names + nameoff;
+ if (!streq(section, n))
+ continue;
+
+ return i;
+ }
+
+ return -ENODATA;
+}
+
+int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, const void **buf, uint64_t *buf_size)
+{
+ uint64_t nameslen;
+ const char *names = elf_get_strings_section(elf, &nameslen);
+ uint16_t i;
+
+ *buf = NULL;
+ *buf_size = 0;
+
+ for (i = 1; i < elf->header.section.count; i++) {
+ uint64_t off, size;
+ uint32_t nameoff;
+ const char *n;
+ int err = elf_get_section_info(elf, i, &off, &size, &nameoff);
+ if (err < 0)
+ continue;
+ if (nameoff >= nameslen)
+ continue;
+ n = names + nameoff;
+ if (!streq(section, n))
+ continue;
+
+ *buf = elf_get_mem(elf, off);
+ *buf_size = size;
+ return 0;
+ }
+
+ return -ENODATA;
+}
+
+/* array will be allocated with strings in a single malloc, just free *array */
+int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array)
+{
+ size_t i, j, count;
+ uint64_t size;
+ const void *buf;
+ const char *strings;
+ char *s, **a;
+ int err;
+
+ *array = NULL;
+
+ err = kmod_elf_get_section(elf, section, &buf, &size);
+ if (err < 0)
+ return err;
+
+ strings = buf;
+ if (strings == NULL || size == 0)
+ return 0;
+
+ /* skip zero padding */
+ while (strings[0] == '\0' && size > 1) {
+ strings++;
+ size--;
+ }
+
+ if (size <= 1)
+ return 0;
+
+ for (i = 0, count = 0; i < size; ) {
+ if (strings[i] != '\0') {
+ i++;
+ continue;
+ }
+
+ while (strings[i] == '\0' && i < size)
+ i++;
+
+ count++;
+ }
+
+ if (strings[i - 1] != '\0')
+ count++;
+
+ *array = a = malloc(size + 1 + sizeof(char *) * (count + 1));
+ if (*array == NULL)
+ return -errno;
+
+ s = (char *)(a + count + 1);
+ memcpy(s, strings, size);
+
+ /* make sure the last string is NULL-terminated */
+ s[size] = '\0';
+ a[count] = NULL;
+ a[0] = s;
+
+ for (i = 0, j = 1; j < count && i < size; ) {
+ if (s[i] != '\0') {
+ i++;
+ continue;
+ }
+
+ while (strings[i] == '\0' && i < size)
+ i++;
+
+ a[j] = &s[i];
+ j++;
+ }
+
+ return count;
+}
+
+/* array will be allocated with strings in a single malloc, just free *array */
+int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array)
+{
+ size_t off, offcrc, slen;
+ uint64_t size;
+ struct kmod_modversion *a;
+ const void *buf;
+ char *itr;
+ int i, count, err;
+#define MODVERSION_SEC_SIZE (sizeof(struct kmod_modversion64))
+
+ assert_cc(sizeof(struct kmod_modversion64) ==
+ sizeof(struct kmod_modversion32));
+
+ if (elf->class & KMOD_ELF_32)
+ offcrc = sizeof(uint32_t);
+ else
+ offcrc = sizeof(uint64_t);
+
+ *array = NULL;
+
+ err = kmod_elf_get_section(elf, "__versions", &buf, &size);
+ if (err < 0)
+ return err;
+
+ if (buf == NULL || size == 0)
+ return 0;
+
+ if (size % MODVERSION_SEC_SIZE != 0)
+ return -EINVAL;
+
+ count = size / MODVERSION_SEC_SIZE;
+
+ off = (const uint8_t *)buf - elf->memory;
+ slen = 0;
+
+ for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
+ const char *symbol = elf_get_mem(elf, off + offcrc);
+
+ if (symbol[0] == '.')
+ symbol++;
+
+ slen += strlen(symbol) + 1;
+ }
+
+ *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
+ if (*array == NULL)
+ return -errno;
+
+ itr = (char *)(a + count);
+ off = (const uint8_t *)buf - elf->memory;
+
+ for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
+ uint64_t crc = elf_get_uint(elf, off, offcrc);
+ const char *symbol = elf_get_mem(elf, off + offcrc);
+ size_t symbollen;
+
+ if (symbol[0] == '.')
+ symbol++;
+
+ a[i].crc = crc;
+ a[i].bind = KMOD_SYMBOL_UNDEF;
+ a[i].symbol = itr;
+ symbollen = strlen(symbol) + 1;
+ memcpy(itr, symbol, symbollen);
+ itr += symbollen;
+ }
+
+ return count;
+}
+
+int kmod_elf_strip_section(struct kmod_elf *elf, const char *section)
+{
+ uint64_t off, size;
+ const void *buf;
+ int idx = elf_find_section(elf, section);
+ uint64_t val;
+
+ if (idx < 0)
+ return idx;
+
+ buf = elf_get_section_header(elf, idx);
+ off = (const uint8_t *)buf - elf->memory;
+
+ if (elf->class & KMOD_ELF_32) {
+ off += offsetof(Elf32_Shdr, sh_flags);
+ size = sizeof(((Elf32_Shdr *)buf)->sh_flags);
+ } else {
+ off += offsetof(Elf64_Shdr, sh_flags);
+ size = sizeof(((Elf64_Shdr *)buf)->sh_flags);
+ }
+
+ val = elf_get_uint(elf, off, size);
+ val &= ~(uint64_t)SHF_ALLOC;
+
+ return elf_set_uint(elf, off, size, val);
+}
+
+int kmod_elf_strip_vermagic(struct kmod_elf *elf)
+{
+ uint64_t i, size;
+ const void *buf;
+ const char *strings;
+ int err;
+
+ err = kmod_elf_get_section(elf, ".modinfo", &buf, &size);
+ if (err < 0)
+ return err;
+ strings = buf;
+ if (strings == NULL || size == 0)
+ return 0;
+
+ /* skip zero padding */
+ while (strings[0] == '\0' && size > 1) {
+ strings++;
+ size--;
+ }
+ if (size <= 1)
+ return 0;
+
+ for (i = 0; i < size; i++) {
+ const char *s;
+ size_t off, len;
+
+ if (strings[i] == '\0')
+ continue;
+ if (i + 1 >= size)
+ continue;
+
+ s = strings + i;
+ len = sizeof("vermagic=") - 1;
+ if (i + len >= size)
+ continue;
+ if (strncmp(s, "vermagic=", len) != 0) {
+ i += strlen(s);
+ continue;
+ }
+ off = (const uint8_t *)s - elf->memory;
+
+ if (elf->changed == NULL) {
+ elf->changed = malloc(elf->size);
+ if (elf->changed == NULL)
+ return -errno;
+ memcpy(elf->changed, elf->memory, elf->size);
+ elf->memory = elf->changed;
+ ELFDBG(elf, "copied memory to allow writing.\n");
+ }
+
+ len = strlen(s);
+ ELFDBG(elf, "clear .modinfo vermagic \"%s\" (%zd bytes)\n",
+ s, len);
+ memset(elf->changed + off, '\0', len);
+ return 0;
+ }
+
+ ELFDBG(elf, "no vermagic found in .modinfo\n");
+ return -ENODATA;
+}
+
+
+static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, struct kmod_modversion **array)
+{
+ uint64_t i, last, size;
+ const void *buf;
+ const char *strings;
+ char *itr;
+ struct kmod_modversion *a;
+ int count, err;
+
+ *array = NULL;
+
+ err = kmod_elf_get_section(elf, "__ksymtab_strings", &buf, &size);
+ if (err < 0)
+ return err;
+ strings = buf;
+ if (strings == NULL || size == 0)
+ return 0;
+
+ /* skip zero padding */
+ while (strings[0] == '\0' && size > 1) {
+ strings++;
+ size--;
+ }
+ if (size <= 1)
+ return 0;
+
+ last = 0;
+ for (i = 0, count = 0; i < size; i++) {
+ if (strings[i] == '\0') {
+ if (last == i) {
+ last = i + 1;
+ continue;
+ }
+ count++;
+ last = i + 1;
+ }
+ }
+ if (strings[i - 1] != '\0')
+ count++;
+
+ *array = a = malloc(size + 1 + sizeof(struct kmod_modversion) * count);
+ if (*array == NULL)
+ return -errno;
+
+ itr = (char *)(a + count);
+ last = 0;
+ for (i = 0, count = 0; i < size; i++) {
+ if (strings[i] == '\0') {
+ size_t slen = i - last;
+ if (last == i) {
+ last = i + 1;
+ continue;
+ }
+ a[count].crc = 0;
+ a[count].bind = KMOD_SYMBOL_GLOBAL;
+ a[count].symbol = itr;
+ memcpy(itr, strings + last, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+ count++;
+ last = i + 1;
+ }
+ }
+ if (strings[i - 1] != '\0') {
+ size_t slen = i - last;
+ a[count].crc = 0;
+ a[count].bind = KMOD_SYMBOL_GLOBAL;
+ a[count].symbol = itr;
+ memcpy(itr, strings + last, slen);
+ itr[slen] = '\0';
+ count++;
+ }
+
+ return count;
+}
+
+static inline uint8_t kmod_symbol_bind_from_elf(uint8_t elf_value)
+{
+ switch (elf_value) {
+ case STB_LOCAL:
+ return KMOD_SYMBOL_LOCAL;
+ case STB_GLOBAL:
+ return KMOD_SYMBOL_GLOBAL;
+ case STB_WEAK:
+ return KMOD_SYMBOL_WEAK;
+ default:
+ return KMOD_SYMBOL_NONE;
+ }
+}
+
+static uint64_t kmod_elf_resolve_crc(const struct kmod_elf *elf, uint64_t crc, uint16_t shndx)
+{
+ int err;
+ uint64_t off, size;
+ uint32_t nameoff;
+
+ if (shndx == SHN_ABS || shndx == SHN_UNDEF)
+ return crc;
+
+ err = elf_get_section_info(elf, shndx, &off, &size, &nameoff);
+ if (err < 0) {
+ ELFDBG("Cound not find section index %"PRIu16" for crc", shndx);
+ return (uint64_t)-1;
+ }
+
+ if (crc > (size - sizeof(uint32_t))) {
+ ELFDBG("CRC offset %"PRIu64" is too big, section %"PRIu16" size is %"PRIu64"\n",
+ crc, shndx, size);
+ return (uint64_t)-1;
+ }
+
+ crc = elf_get_uint(elf, off + crc, sizeof(uint32_t));
+ return crc;
+}
+
+/* array will be allocated with strings in a single malloc, just free *array */
+int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array)
+{
+ static const char crc_str[] = "__crc_";
+ static const size_t crc_strlen = sizeof(crc_str) - 1;
+ uint64_t strtablen, symtablen, str_off, sym_off;
+ const void *strtab, *symtab;
+ struct kmod_modversion *a;
+ char *itr;
+ size_t slen, symlen;
+ int i, count, symcount, err;
+
+ err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .strtab found.\n");
+ goto fallback;
+ }
+
+ err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .symtab found.\n");
+ goto fallback;
+ }
+
+ if (elf->class & KMOD_ELF_32)
+ symlen = sizeof(Elf32_Sym);
+ else
+ symlen = sizeof(Elf64_Sym);
+
+ if (symtablen % symlen != 0) {
+ ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" as expected.\n", symtablen, symlen);
+ goto fallback;
+ }
+
+ symcount = symtablen / symlen;
+ count = 0;
+ slen = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + symlen;
+ for (i = 1; i < symcount; i++, sym_off += symlen) {
+ const char *name;
+ uint32_t name_off;
+
+#define READV(field) \
+ elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
+ sizeof(s->field))
+ if (elf->class & KMOD_ELF_32) {
+ Elf32_Sym *s;
+ name_off = READV(st_name);
+ } else {
+ Elf64_Sym *s;
+ name_off = READV(st_name);
+ }
+#undef READV
+ if (name_off >= strtablen) {
+ ELFDBG(elf, ".strtab is %"PRIu64" bytes, but .symtab entry %d wants to access offset %"PRIu32".\n", strtablen, i, name_off);
+ goto fallback;
+ }
+
+ name = elf_get_mem(elf, str_off + name_off);
+
+ if (strncmp(name, crc_str, crc_strlen) != 0)
+ continue;
+ slen += strlen(name + crc_strlen) + 1;
+ count++;
+ }
+
+ if (count == 0)
+ goto fallback;
+
+ *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
+ if (*array == NULL)
+ return -errno;
+
+ itr = (char *)(a + count);
+ count = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + symlen;
+ for (i = 1; i < symcount; i++, sym_off += symlen) {
+ const char *name;
+ uint32_t name_off;
+ uint64_t crc;
+ uint8_t info, bind;
+ uint16_t shndx;
+
+#define READV(field) \
+ elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
+ sizeof(s->field))
+ if (elf->class & KMOD_ELF_32) {
+ Elf32_Sym *s;
+ name_off = READV(st_name);
+ crc = READV(st_value);
+ info = READV(st_info);
+ shndx = READV(st_shndx);
+ } else {
+ Elf64_Sym *s;
+ name_off = READV(st_name);
+ crc = READV(st_value);
+ info = READV(st_info);
+ shndx = READV(st_shndx);
+ }
+#undef READV
+ name = elf_get_mem(elf, str_off + name_off);
+ if (strncmp(name, crc_str, crc_strlen) != 0)
+ continue;
+ name += crc_strlen;
+
+ if (elf->class & KMOD_ELF_32)
+ bind = ELF32_ST_BIND(info);
+ else
+ bind = ELF64_ST_BIND(info);
+
+ a[count].crc = kmod_elf_resolve_crc(elf, crc, shndx);
+ a[count].bind = kmod_symbol_bind_from_elf(bind);
+ a[count].symbol = itr;
+ slen = strlen(name);
+ memcpy(itr, name, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+ count++;
+ }
+ return count;
+
+fallback:
+ ELFDBG(elf, "Falling back to __ksymtab_strings!\n");
+ return kmod_elf_get_symbols_symtab(elf, array);
+}
+
+static int kmod_elf_crc_find(const struct kmod_elf *elf, const void *versions, uint64_t versionslen, const char *name, uint64_t *crc)
+{
+ size_t verlen, crclen, off;
+ uint64_t i;
+
+ if (elf->class & KMOD_ELF_32) {
+ struct kmod_modversion32 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ } else {
+ struct kmod_modversion64 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ }
+
+ off = (const uint8_t *)versions - elf->memory;
+ for (i = 0; i < versionslen; i += verlen) {
+ const char *symbol = elf_get_mem(elf, off + i + crclen);
+ if (!streq(name, symbol))
+ continue;
+ *crc = elf_get_uint(elf, off + i, crclen);
+ return i / verlen;
+ }
+
+ ELFDBG(elf, "could not find crc for symbol '%s'\n", name);
+ *crc = 0;
+ return -1;
+}
+
+/* from module-init-tools:elfops_core.c */
+#ifndef STT_REGISTER
+#define STT_REGISTER 13 /* Global register reserved to app. */
+#endif
+
+/* array will be allocated with strings in a single malloc, just free *array */
+int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, struct kmod_modversion **array)
+{
+ uint64_t versionslen, strtablen, symtablen, str_off, sym_off, ver_off;
+ const void *versions, *strtab, *symtab;
+ struct kmod_modversion *a;
+ char *itr;
+ size_t slen, verlen, symlen, crclen;
+ int i, count, symcount, vercount, err;
+ bool handle_register_symbols;
+ uint8_t *visited_versions;
+ uint64_t *symcrcs;
+
+ err = kmod_elf_get_section(elf, "__versions", &versions, &versionslen);
+ if (err < 0) {
+ versions = NULL;
+ versionslen = 0;
+ verlen = 0;
+ crclen = 0;
+ } else {
+ if (elf->class & KMOD_ELF_32) {
+ struct kmod_modversion32 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ } else {
+ struct kmod_modversion64 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ }
+ if (versionslen % verlen != 0) {
+ ELFDBG(elf, "unexpected __versions of length %"PRIu64", not multiple of %zd as expected.\n", versionslen, verlen);
+ versions = NULL;
+ versionslen = 0;
+ }
+ }
+
+ err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .strtab found.\n");
+ return -EINVAL;
+ }
+
+ err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .symtab found.\n");
+ return -EINVAL;
+ }
+
+ if (elf->class & KMOD_ELF_32)
+ symlen = sizeof(Elf32_Sym);
+ else
+ symlen = sizeof(Elf64_Sym);
+
+ if (symtablen % symlen != 0) {
+ ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" as expected.\n", symtablen, symlen);
+ return -EINVAL;
+ }
+
+ if (versionslen == 0) {
+ vercount = 0;
+ visited_versions = NULL;
+ } else {
+ vercount = versionslen / verlen;
+ visited_versions = calloc(vercount, sizeof(uint8_t));
+ if (visited_versions == NULL)
+ return -ENOMEM;
+ }
+
+ handle_register_symbols = (elf->header.machine == EM_SPARC ||
+ elf->header.machine == EM_SPARCV9);
+
+ symcount = symtablen / symlen;
+ count = 0;
+ slen = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + symlen;
+
+ symcrcs = calloc(symcount, sizeof(uint64_t));
+ if (symcrcs == NULL) {
+ free(visited_versions);
+ return -ENOMEM;
+ }
+
+ for (i = 1; i < symcount; i++, sym_off += symlen) {
+ const char *name;
+ uint64_t crc;
+ uint32_t name_off;
+ uint16_t secidx;
+ uint8_t info;
+ int idx;
+
+#define READV(field) \
+ elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
+ sizeof(s->field))
+ if (elf->class & KMOD_ELF_32) {
+ Elf32_Sym *s;
+ name_off = READV(st_name);
+ secidx = READV(st_shndx);
+ info = READV(st_info);
+ } else {
+ Elf64_Sym *s;
+ name_off = READV(st_name);
+ secidx = READV(st_shndx);
+ info = READV(st_info);
+ }
+#undef READV
+ if (secidx != SHN_UNDEF)
+ continue;
+
+ if (handle_register_symbols) {
+ uint8_t type;
+ if (elf->class & KMOD_ELF_32)
+ type = ELF32_ST_TYPE(info);
+ else
+ type = ELF64_ST_TYPE(info);
+
+ /* Not really undefined: sparc gcc 3.3 creates
+ * U references when you have global asm
+ * variables, to avoid anyone else misusing
+ * them.
+ */
+ if (type == STT_REGISTER)
+ continue;
+ }
+
+ if (name_off >= strtablen) {
+ ELFDBG(elf, ".strtab is %"PRIu64" bytes, but .symtab entry %d wants to access offset %"PRIu32".\n", strtablen, i, name_off);
+ free(visited_versions);
+ free(symcrcs);
+ return -EINVAL;
+ }
+
+ name = elf_get_mem(elf, str_off + name_off);
+ if (name[0] == '\0') {
+ ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", i);
+ continue;
+ }
+
+ slen += strlen(name) + 1;
+ count++;
+
+ idx = kmod_elf_crc_find(elf, versions, versionslen, name, &crc);
+ if (idx >= 0 && visited_versions != NULL)
+ visited_versions[idx] = 1;
+ symcrcs[i] = crc;
+ }
+
+ if (visited_versions != NULL) {
+ /* module_layout/struct_module are not visited, but needed */
+ ver_off = (const uint8_t *)versions - elf->memory;
+ for (i = 0; i < vercount; i++) {
+ if (visited_versions[i] == 0) {
+ const char *name;
+ name = elf_get_mem(elf, ver_off + i * verlen + crclen);
+ slen += strlen(name) + 1;
+
+ count++;
+ }
+ }
+ }
+
+ if (count == 0) {
+ free(visited_versions);
+ free(symcrcs);
+ *array = NULL;
+ return 0;
+ }
+
+ *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
+ if (*array == NULL) {
+ free(visited_versions);
+ free(symcrcs);
+ return -errno;
+ }
+
+ itr = (char *)(a + count);
+ count = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + symlen;
+ for (i = 1; i < symcount; i++, sym_off += symlen) {
+ const char *name;
+ uint64_t crc;
+ uint32_t name_off;
+ uint16_t secidx;
+ uint8_t info, bind;
+
+#define READV(field) \
+ elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
+ sizeof(s->field))
+ if (elf->class & KMOD_ELF_32) {
+ Elf32_Sym *s;
+ name_off = READV(st_name);
+ secidx = READV(st_shndx);
+ info = READV(st_info);
+ } else {
+ Elf64_Sym *s;
+ name_off = READV(st_name);
+ secidx = READV(st_shndx);
+ info = READV(st_info);
+ }
+#undef READV
+ if (secidx != SHN_UNDEF)
+ continue;
+
+ if (handle_register_symbols) {
+ uint8_t type;
+ if (elf->class & KMOD_ELF_32)
+ type = ELF32_ST_TYPE(info);
+ else
+ type = ELF64_ST_TYPE(info);
+
+ /* Not really undefined: sparc gcc 3.3 creates
+ * U references when you have global asm
+ * variables, to avoid anyone else misusing
+ * them.
+ */
+ if (type == STT_REGISTER)
+ continue;
+ }
+
+ name = elf_get_mem(elf, str_off + name_off);
+ if (name[0] == '\0') {
+ ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", i);
+ continue;
+ }
+
+ if (elf->class & KMOD_ELF_32)
+ bind = ELF32_ST_BIND(info);
+ else
+ bind = ELF64_ST_BIND(info);
+ if (bind == STB_WEAK)
+ bind = KMOD_SYMBOL_WEAK;
+ else
+ bind = KMOD_SYMBOL_UNDEF;
+
+ slen = strlen(name);
+ crc = symcrcs[i];
+
+ a[count].crc = crc;
+ a[count].bind = bind;
+ a[count].symbol = itr;
+ memcpy(itr, name, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+
+ count++;
+ }
+
+ free(symcrcs);
+
+ if (visited_versions == NULL)
+ return count;
+
+ /* add unvisited (module_layout/struct_module) */
+ ver_off = (const uint8_t *)versions - elf->memory;
+ for (i = 0; i < vercount; i++) {
+ const char *name;
+ uint64_t crc;
+
+ if (visited_versions[i] != 0)
+ continue;
+
+ name = elf_get_mem(elf, ver_off + i * verlen + crclen);
+ slen = strlen(name);
+ crc = elf_get_uint(elf, ver_off + i * verlen, crclen);
+
+ a[count].crc = crc;
+ a[count].bind = KMOD_SYMBOL_UNDEF;
+ a[count].symbol = itr;
+ memcpy(itr, name, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+
+ count++;
+ }
+ free(visited_versions);
+ return count;
+}
diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c
new file mode 100644
index 0000000..b138e7e
--- /dev/null
+++ b/libkmod/libkmod-file.c
@@ -0,0 +1,541 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef ENABLE_ZSTD
+#include <zstd.h>
+#endif
+#ifdef ENABLE_XZ
+#include <lzma.h>
+#endif
+#ifdef ENABLE_ZLIB
+#include <zlib.h>
+#endif
+
+#include <shared/util.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+struct kmod_file;
+struct file_ops {
+ int (*load)(struct kmod_file *file);
+ void (*unload)(struct kmod_file *file);
+};
+
+struct kmod_file {
+#ifdef ENABLE_ZSTD
+ bool zstd_used;
+#endif
+#ifdef ENABLE_XZ
+ bool xz_used;
+#endif
+#ifdef ENABLE_ZLIB
+ gzFile gzf;
+#endif
+ int fd;
+ enum kmod_file_compression_type compression;
+ off_t size;
+ void *memory;
+ const struct file_ops *ops;
+ const struct kmod_ctx *ctx;
+ struct kmod_elf *elf;
+};
+
+#ifdef ENABLE_ZSTD
+static int zstd_read_block(struct kmod_file *file, size_t block_size,
+ ZSTD_inBuffer *input, size_t *input_capacity)
+{
+ ssize_t rdret;
+ int ret;
+
+ if (*input_capacity < block_size) {
+ free((void *)input->src);
+ input->src = malloc(block_size);
+ if (input->src == NULL) {
+ ret = -errno;
+ ERR(file->ctx, "zstd: %m\n");
+ return ret;
+ }
+ *input_capacity = block_size;
+ }
+
+ rdret = read(file->fd, (void *)input->src, block_size);
+ if (rdret < 0) {
+ ret = -errno;
+ ERR(file->ctx, "zstd: %m\n");
+ return ret;
+ }
+
+ input->pos = 0;
+ input->size = rdret;
+ return 0;
+}
+
+static int zstd_ensure_outbuffer_space(ZSTD_outBuffer *buffer, size_t min_free)
+{
+ uint8_t *old_buffer = buffer->dst;
+ int ret = 0;
+
+ if (buffer->size - buffer->pos >= min_free)
+ return 0;
+
+ buffer->size += min_free;
+ buffer->dst = realloc(buffer->dst, buffer->size);
+ if (buffer->dst == NULL) {
+ ret = -errno;
+ free(old_buffer);
+ }
+
+ return ret;
+}
+
+static int zstd_decompress_block(struct kmod_file *file, ZSTD_DStream *dstr,
+ ZSTD_inBuffer *input, ZSTD_outBuffer *output,
+ size_t *next_block_size)
+{
+ size_t out_buf_min_size = ZSTD_DStreamOutSize();
+ int ret = 0;
+
+ do {
+ ssize_t dsret;
+
+ ret = zstd_ensure_outbuffer_space(output, out_buf_min_size);
+ if (ret) {
+ ERR(file->ctx, "zstd: %s\n", strerror(-ret));
+ break;
+ }
+
+ dsret = ZSTD_decompressStream(dstr, output, input);
+ if (ZSTD_isError(dsret)) {
+ ret = -EINVAL;
+ ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(dsret));
+ break;
+ }
+ if (dsret > 0)
+ *next_block_size = (size_t)dsret;
+ } while (input->pos < input->size
+ || output->pos > output->size
+ || output->size - output->pos < out_buf_min_size);
+
+ return ret;
+}
+
+static int load_zstd(struct kmod_file *file)
+{
+ ZSTD_DStream *dstr;
+ size_t next_block_size;
+ size_t zst_inb_capacity = 0;
+ ZSTD_inBuffer zst_inb = { 0 };
+ ZSTD_outBuffer zst_outb = { 0 };
+ int ret;
+
+ dstr = ZSTD_createDStream();
+ if (dstr == NULL) {
+ ret = -EINVAL;
+ ERR(file->ctx, "zstd: Failed to create decompression stream\n");
+ goto out;
+ }
+
+ next_block_size = ZSTD_initDStream(dstr);
+
+ while (true) {
+ ret = zstd_read_block(file, next_block_size, &zst_inb,
+ &zst_inb_capacity);
+ if (ret != 0)
+ goto out;
+ if (zst_inb.size == 0) /* EOF */
+ break;
+
+ ret = zstd_decompress_block(file, dstr, &zst_inb, &zst_outb,
+ &next_block_size);
+ if (ret != 0)
+ goto out;
+ }
+
+ ZSTD_freeDStream(dstr);
+ free((void *)zst_inb.src);
+ file->zstd_used = true;
+ file->memory = zst_outb.dst;
+ file->size = zst_outb.pos;
+ return 0;
+out:
+ if (dstr != NULL)
+ ZSTD_freeDStream(dstr);
+ free((void *)zst_inb.src);
+ free((void *)zst_outb.dst);
+ return ret;
+}
+
+static void unload_zstd(struct kmod_file *file)
+{
+ if (!file->zstd_used)
+ return;
+ free(file->memory);
+}
+
+static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD};
+#endif
+
+#ifdef ENABLE_XZ
+static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
+{
+ switch (ret) {
+ case LZMA_MEM_ERROR:
+ ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
+ break;
+ case LZMA_FORMAT_ERROR:
+ ERR(file->ctx, "xz: File format not recognized\n");
+ break;
+ case LZMA_OPTIONS_ERROR:
+ ERR(file->ctx, "xz: Unsupported compression options\n");
+ break;
+ case LZMA_DATA_ERROR:
+ ERR(file->ctx, "xz: File is corrupt\n");
+ break;
+ case LZMA_BUF_ERROR:
+ ERR(file->ctx, "xz: Unexpected end of input\n");
+ break;
+ default:
+ ERR(file->ctx, "xz: Internal error (bug)\n");
+ break;
+ }
+}
+
+static int xz_uncompress(lzma_stream *strm, struct kmod_file *file)
+{
+ uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ];
+ lzma_action action = LZMA_RUN;
+ lzma_ret ret;
+ void *p = NULL;
+ size_t total = 0;
+
+ strm->avail_in = 0;
+ strm->next_out = out_buf;
+ strm->avail_out = sizeof(out_buf);
+
+ while (true) {
+ if (strm->avail_in == 0) {
+ ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf));
+ if (rdret < 0) {
+ ret = -errno;
+ goto out;
+ }
+ strm->next_in = in_buf;
+ strm->avail_in = rdret;
+ if (rdret == 0)
+ action = LZMA_FINISH;
+ }
+ ret = lzma_code(strm, action);
+ if (strm->avail_out == 0 || ret != LZMA_OK) {
+ size_t write_size = BUFSIZ - strm->avail_out;
+ char *tmp = realloc(p, total + write_size);
+ if (tmp == NULL) {
+ ret = -errno;
+ goto out;
+ }
+ memcpy(tmp + total, out_buf, write_size);
+ total += write_size;
+ p = tmp;
+ strm->next_out = out_buf;
+ strm->avail_out = BUFSIZ;
+ }
+ if (ret == LZMA_STREAM_END)
+ break;
+ if (ret != LZMA_OK) {
+ xz_uncompress_belch(file, ret);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ file->xz_used = true;
+ file->memory = p;
+ file->size = total;
+ return 0;
+ out:
+ free(p);
+ return ret;
+}
+
+static int load_xz(struct kmod_file *file)
+{
+ lzma_stream strm = LZMA_STREAM_INIT;
+ lzma_ret lzret;
+ int ret;
+
+ lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
+ if (lzret == LZMA_MEM_ERROR) {
+ ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
+ return -ENOMEM;
+ } else if (lzret != LZMA_OK) {
+ ERR(file->ctx, "xz: Internal error (bug)\n");
+ return -EINVAL;
+ }
+ ret = xz_uncompress(&strm, file);
+ lzma_end(&strm);
+ return ret;
+}
+
+static void unload_xz(struct kmod_file *file)
+{
+ if (!file->xz_used)
+ return;
+ free(file->memory);
+}
+
+static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
+#endif
+
+#ifdef ENABLE_ZLIB
+#define READ_STEP (4 * 1024 * 1024)
+static int load_zlib(struct kmod_file *file)
+{
+ int err = 0;
+ off_t did = 0, total = 0;
+ _cleanup_free_ unsigned char *p = NULL;
+
+ errno = 0;
+ file->gzf = gzdopen(file->fd, "rb");
+ if (file->gzf == NULL)
+ return -errno;
+ file->fd = -1; /* now owned by gzf due gzdopen() */
+
+ for (;;) {
+ int r;
+
+ if (did == total) {
+ void *tmp = realloc(p, total + READ_STEP);
+ if (tmp == NULL) {
+ err = -errno;
+ goto error;
+ }
+ total += READ_STEP;
+ p = tmp;
+ }
+
+ r = gzread(file->gzf, p + did, total - did);
+ if (r == 0)
+ break;
+ else if (r < 0) {
+ int gzerr;
+ const char *gz_errmsg = gzerror(file->gzf, &gzerr);
+
+ ERR(file->ctx, "gzip: %s\n", gz_errmsg);
+
+ /* gzip might not set errno here */
+ err = gzerr == Z_ERRNO ? -errno : -EINVAL;
+ goto error;
+ }
+ did += r;
+ }
+
+ file->memory = p;
+ file->size = did;
+ p = NULL;
+ return 0;
+
+error:
+ gzclose(file->gzf);
+ return err;
+}
+
+static void unload_zlib(struct kmod_file *file)
+{
+ if (file->gzf == NULL)
+ return;
+ free(file->memory);
+ gzclose(file->gzf); /* closes file->fd */
+}
+
+static const char magic_zlib[] = {0x1f, 0x8b};
+#endif
+
+static const struct comp_type {
+ size_t magic_size;
+ enum kmod_file_compression_type compression;
+ const char *magic_bytes;
+ const struct file_ops ops;
+} comp_types[] = {
+#ifdef ENABLE_ZSTD
+ {sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, {load_zstd, unload_zstd}},
+#endif
+#ifdef ENABLE_XZ
+ {sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, {load_xz, unload_xz}},
+#endif
+#ifdef ENABLE_ZLIB
+ {sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, {load_zlib, unload_zlib}},
+#endif
+ {0, KMOD_FILE_COMPRESSION_NONE, NULL, {NULL, NULL}}
+};
+
+static int load_reg(struct kmod_file *file)
+{
+ struct stat st;
+
+ if (fstat(file->fd, &st) < 0)
+ return -errno;
+
+ file->size = st.st_size;
+ file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
+ file->fd, 0);
+ if (file->memory == MAP_FAILED)
+ return -errno;
+
+ return 0;
+}
+
+static void unload_reg(struct kmod_file *file)
+{
+ munmap(file->memory, file->size);
+}
+
+static const struct file_ops reg_ops = {
+ load_reg, unload_reg
+};
+
+struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
+{
+ if (file->elf)
+ return file->elf;
+
+ kmod_file_load_contents(file);
+ file->elf = kmod_elf_new(file->memory, file->size);
+ return file->elf;
+}
+
+struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
+ const char *filename)
+{
+ struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
+ const struct comp_type *itr;
+ size_t magic_size_max = 0;
+ int err = 0;
+
+ if (file == NULL)
+ return NULL;
+
+ file->fd = open(filename, O_RDONLY|O_CLOEXEC);
+ if (file->fd < 0) {
+ err = -errno;
+ goto error;
+ }
+
+ for (itr = comp_types; itr->ops.load != NULL; itr++) {
+ if (magic_size_max < itr->magic_size)
+ magic_size_max = itr->magic_size;
+ }
+
+ if (magic_size_max > 0) {
+ char *buf = alloca(magic_size_max + 1);
+ ssize_t sz;
+
+ if (buf == NULL) {
+ err = -errno;
+ goto error;
+ }
+ sz = read_str_safe(file->fd, buf, magic_size_max + 1);
+ lseek(file->fd, 0, SEEK_SET);
+ if (sz != (ssize_t)magic_size_max) {
+ if (sz < 0)
+ err = sz;
+ else
+ err = -EINVAL;
+ goto error;
+ }
+
+ for (itr = comp_types; itr->ops.load != NULL; itr++) {
+ if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) {
+ file->ops = &itr->ops;
+ file->compression = itr->compression;
+ break;
+ }
+ }
+ }
+
+ if (file->ops == NULL) {
+ file->ops = &reg_ops;
+ file->compression = KMOD_FILE_COMPRESSION_NONE;
+ }
+
+ file->ctx = ctx;
+
+error:
+ if (err < 0) {
+ if (file->fd >= 0)
+ close(file->fd);
+ free(file);
+ errno = -err;
+ return NULL;
+ }
+
+ return file;
+}
+
+/*
+ * Callers should just check file->memory got updated
+ */
+void kmod_file_load_contents(struct kmod_file *file)
+{
+ if (file->memory)
+ return;
+
+ /* The load functions already log possible errors. */
+ file->ops->load(file);
+}
+
+void *kmod_file_get_contents(const struct kmod_file *file)
+{
+ return file->memory;
+}
+
+off_t kmod_file_get_size(const struct kmod_file *file)
+{
+ return file->size;
+}
+
+enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file)
+{
+ return file->compression;
+}
+
+int kmod_file_get_fd(const struct kmod_file *file)
+{
+ return file->fd;
+}
+
+void kmod_file_unref(struct kmod_file *file)
+{
+ if (file->elf)
+ kmod_elf_unref(file->elf);
+
+ if (file->memory)
+ file->ops->unload(file);
+
+ if (file->fd >= 0)
+ close(file->fd);
+ free(file);
+}
diff --git a/libkmod/libkmod-index.c b/libkmod/libkmod-index.c
new file mode 100644
index 0000000..6a34c8d
--- /dev/null
+++ b/libkmod/libkmod-index.c
@@ -0,0 +1,1082 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/macro.h>
+#include <shared/strbuf.h>
+#include <shared/util.h>
+
+#include "libkmod-internal.h"
+#include "libkmod-index.h"
+
+/* libkmod-index.c: module index file implementation
+ *
+ * Integers are stored as 32 bit unsigned in "network" order, i.e. MSB first.
+ * All files start with a magic number.
+ *
+ * Magic spells "BOOTFAST". Second one used on newer versioned binary files.
+ * #define INDEX_MAGIC_OLD 0xB007FA57
+ *
+ * We use a version string to keep track of changes to the binary format
+ * This is stored in the form: INDEX_MAJOR (hi) INDEX_MINOR (lo) just in
+ * case we ever decide to have minor changes that are not incompatible.
+ */
+#define INDEX_MAGIC 0xB007F457
+#define INDEX_VERSION_MAJOR 0x0002
+#define INDEX_VERSION_MINOR 0x0001
+#define INDEX_VERSION ((INDEX_VERSION_MAJOR<<16)|INDEX_VERSION_MINOR)
+
+/* The index file maps keys to values. Both keys and values are ASCII strings.
+ * Each key can have multiple values. Values are sorted by an integer priority.
+ *
+ * The reader also implements a wildcard search (including range expressions)
+ * where the keys in the index are treated as patterns.
+ * This feature is required for module aliases.
+ */
+#define INDEX_CHILDMAX 128
+
+/* Disk format:
+ *
+ * uint32_t magic = INDEX_MAGIC;
+ * uint32_t version = INDEX_VERSION;
+ * uint32_t root_offset;
+ *
+ * (node_offset & INDEX_NODE_MASK) specifies the file offset of nodes:
+ *
+ * char[] prefix; // nul terminated
+ *
+ * char first;
+ * char last;
+ * uint32_t children[last - first + 1];
+ *
+ * uint32_t value_count;
+ * struct {
+ * uint32_t priority;
+ * char[] value; // nul terminated
+ * } values[value_count];
+ *
+ * (node_offset & INDEX_NODE_FLAGS) indicates which fields are present.
+ * Empty prefixes are omitted, leaf nodes omit the three child-related fields.
+ *
+ * This could be optimised further by adding a sparse child format
+ * (indicated using a new flag).
+ *
+ *
+ * Implementation is based on a radix tree, or "trie".
+ * Each arc from parent to child is labelled with a character.
+ * Each path from the root represents a string.
+ *
+ * == Example strings ==
+ *
+ * ask
+ * ate
+ * on
+ * once
+ * one
+ *
+ * == Key ==
+ * + Normal node
+ * * Marked node, representing a key and it's values.
+ *
+ * +
+ * |-a-+-s-+-k-*
+ * | |
+ * | `-t-+-e-*
+ * |
+ * `-o-+-n-*-c-+-e-*
+ * |
+ * `-e-*
+ *
+ * Naive implementations tend to be very space inefficient; child pointers
+ * are stored in arrays indexed by character, but most child pointers are null.
+ *
+ * Our implementation uses a scheme described by Wikipedia as a Patrica trie,
+ *
+ * "easiest to understand as a space-optimized trie where
+ * each node with only one child is merged with its child"
+ *
+ * +
+ * |-a-+-sk-*
+ * | |
+ * | `-te-*
+ * |
+ * `-on-*-ce-*
+ * |
+ * `-e-*
+ *
+ * We still use arrays of child pointers indexed by a single character;
+ * the remaining characters of the label are stored as a "prefix" in the child.
+ *
+ * The paper describing the original Patrica trie works on individiual bits -
+ * each node has a maximum of two children, which increases space efficiency.
+ * However for this application it is simpler to use the ASCII character set.
+ * Since the index file is read-only, it can be compressed by omitting null
+ * child pointers at the start and end of arrays.
+ */
+
+/* Format of node offsets within index file */
+enum node_offset {
+ INDEX_NODE_FLAGS = 0xF0000000, /* Flags in high nibble */
+ INDEX_NODE_PREFIX = 0x80000000,
+ INDEX_NODE_VALUES = 0x40000000,
+ INDEX_NODE_CHILDS = 0x20000000,
+
+ INDEX_NODE_MASK = 0x0FFFFFFF, /* Offset value */
+};
+
+void index_values_free(struct index_value *values)
+{
+ while (values) {
+ struct index_value *value = values;
+
+ values = value->next;
+ free(value);
+ }
+}
+
+static int add_value(struct index_value **values,
+ const char *value, unsigned len, unsigned int priority)
+{
+ struct index_value *v;
+
+ /* find position to insert value */
+ while (*values && (*values)->priority < priority)
+ values = &(*values)->next;
+
+ v = malloc(sizeof(struct index_value) + len + 1);
+ if (!v)
+ return -1;
+ v->next = *values;
+ v->priority = priority;
+ v->len = len;
+ memcpy(v->value, value, len);
+ v->value[len] = '\0';
+ *values = v;
+
+ return 0;
+}
+
+static void read_error(void)
+{
+ fatal("Module index: unexpected error: %s\n"
+ "Try re-running depmod\n", errno ? strerror(errno) : "EOF");
+}
+
+static int read_char(FILE *in)
+{
+ int ch;
+
+ errno = 0;
+ ch = getc_unlocked(in);
+ if (ch == EOF)
+ read_error();
+ return ch;
+}
+
+static uint32_t read_long(FILE *in)
+{
+ uint32_t l;
+
+ errno = 0;
+ if (fread(&l, sizeof(uint32_t), 1, in) != sizeof(uint32_t))
+ read_error();
+ return ntohl(l);
+}
+
+static unsigned buf_freadchars(struct strbuf *buf, FILE *in)
+{
+ unsigned i = 0;
+ int ch;
+
+ while ((ch = read_char(in))) {
+ if (!strbuf_pushchar(buf, ch))
+ break;
+ i++;
+ }
+
+ return i;
+}
+
+/*
+ * Index file searching
+ */
+struct index_node_f {
+ FILE *file;
+ char *prefix; /* path compression */
+ struct index_value *values;
+ unsigned char first; /* range of child nodes */
+ unsigned char last;
+ uint32_t children[0];
+};
+
+static struct index_node_f *index_read(FILE *in, uint32_t offset)
+{
+ struct index_node_f *node;
+ char *prefix;
+ int i, child_count = 0;
+
+ if ((offset & INDEX_NODE_MASK) == 0)
+ return NULL;
+
+ if (fseek(in, offset & INDEX_NODE_MASK, SEEK_SET) < 0)
+ return NULL;
+
+ if (offset & INDEX_NODE_PREFIX) {
+ struct strbuf buf;
+ strbuf_init(&buf);
+ buf_freadchars(&buf, in);
+ prefix = strbuf_steal(&buf);
+ } else
+ prefix = NOFAIL(strdup(""));
+
+ if (offset & INDEX_NODE_CHILDS) {
+ char first = read_char(in);
+ char last = read_char(in);
+ child_count = last - first + 1;
+
+ node = NOFAIL(malloc(sizeof(struct index_node_f) +
+ sizeof(uint32_t) * child_count));
+
+ node->first = first;
+ node->last = last;
+
+ for (i = 0; i < child_count; i++)
+ node->children[i] = read_long(in);
+ } else {
+ node = NOFAIL(malloc(sizeof(struct index_node_f)));
+ node->first = INDEX_CHILDMAX;
+ node->last = 0;
+ }
+
+ node->values = NULL;
+ if (offset & INDEX_NODE_VALUES) {
+ int value_count;
+ struct strbuf buf;
+ const char *value;
+ unsigned int priority;
+
+ value_count = read_long(in);
+
+ strbuf_init(&buf);
+ while (value_count--) {
+ priority = read_long(in);
+ buf_freadchars(&buf, in);
+ value = strbuf_str(&buf);
+ add_value(&node->values, value, buf.used, priority);
+ strbuf_clear(&buf);
+ }
+ strbuf_release(&buf);
+ }
+
+ node->prefix = prefix;
+ node->file = in;
+ return node;
+}
+
+static void index_close(struct index_node_f *node)
+{
+ free(node->prefix);
+ index_values_free(node->values);
+ free(node);
+}
+
+struct index_file {
+ FILE *file;
+ uint32_t root_offset;
+};
+
+struct index_file *index_file_open(const char *filename)
+{
+ FILE *file;
+ uint32_t magic, version;
+ struct index_file *new;
+
+ file = fopen(filename, "re");
+ if (!file)
+ return NULL;
+ errno = EINVAL;
+
+ magic = read_long(file);
+ if (magic != INDEX_MAGIC) {
+ fclose(file);
+ return NULL;
+ }
+
+ version = read_long(file);
+ if (version >> 16 != INDEX_VERSION_MAJOR) {
+ fclose(file);
+ return NULL;
+ }
+
+ new = NOFAIL(malloc(sizeof(struct index_file)));
+ new->file = file;
+ new->root_offset = read_long(new->file);
+
+ errno = 0;
+ return new;
+}
+
+void index_file_close(struct index_file *idx)
+{
+ fclose(idx->file);
+ free(idx);
+}
+
+static struct index_node_f *index_readroot(struct index_file *in)
+{
+ return index_read(in->file, in->root_offset);
+}
+
+static struct index_node_f *index_readchild(const struct index_node_f *parent,
+ int ch)
+{
+ if (parent->first <= ch && ch <= parent->last) {
+ return index_read(parent->file,
+ parent->children[ch - parent->first]);
+ }
+
+ return NULL;
+}
+
+static void index_dump_node(struct index_node_f *node, struct strbuf *buf,
+ int fd)
+{
+ struct index_value *v;
+ int ch, pushed;
+
+ pushed = strbuf_pushchars(buf, node->prefix);
+
+ for (v = node->values; v != NULL; v = v->next) {
+ write_str_safe(fd, buf->bytes, buf->used);
+ write_str_safe(fd, " ", 1);
+ write_str_safe(fd, v->value, strlen(v->value));
+ write_str_safe(fd, "\n", 1);
+ }
+
+ for (ch = node->first; ch <= node->last; ch++) {
+ struct index_node_f *child = index_readchild(node, ch);
+
+ if (!child)
+ continue;
+
+ strbuf_pushchar(buf, ch);
+ index_dump_node(child, buf, fd);
+ strbuf_popchar(buf);
+ }
+
+ strbuf_popchars(buf, pushed);
+ index_close(node);
+}
+
+void index_dump(struct index_file *in, int fd, const char *prefix)
+{
+ struct index_node_f *root;
+ struct strbuf buf;
+
+ root = index_readroot(in);
+ if (root == NULL)
+ return;
+
+ strbuf_init(&buf);
+ strbuf_pushchars(&buf, prefix);
+ index_dump_node(root, &buf, fd);
+ strbuf_release(&buf);
+}
+
+static char *index_search__node(struct index_node_f *node, const char *key, int i)
+{
+ char *value;
+ struct index_node_f *child;
+ int ch;
+ int j;
+
+ while(node) {
+ for (j = 0; node->prefix[j]; j++) {
+ ch = node->prefix[j];
+
+ if (ch != key[i+j]) {
+ index_close(node);
+ return NULL;
+ }
+ }
+
+ i += j;
+
+ if (key[i] == '\0') {
+ value = node->values != NULL
+ ? strdup(node->values[0].value)
+ : NULL;
+
+ index_close(node);
+ return value;
+ }
+
+ child = index_readchild(node, key[i]);
+ index_close(node);
+ node = child;
+ i++;
+ }
+
+ return NULL;
+}
+
+/*
+ * Search the index for a key
+ *
+ * Returns the value of the first match
+ *
+ * The recursive functions free their node argument (using index_close).
+ */
+char *index_search(struct index_file *in, const char *key)
+{
+// FIXME: return value by reference instead of strdup
+ struct index_node_f *root;
+ char *value;
+
+ root = index_readroot(in);
+ value = index_search__node(root, key, 0);
+
+ return value;
+}
+
+
+
+/* Level 4: add all the values from a matching node */
+static void index_searchwild__allvalues(struct index_node_f *node,
+ struct index_value **out)
+{
+ struct index_value *v;
+
+ for (v = node->values; v != NULL; v = v->next)
+ add_value(out, v->value, v->len, v->priority);
+
+ index_close(node);
+}
+
+/*
+ * Level 3: traverse a sub-keyspace which starts with a wildcard,
+ * looking for matches.
+ */
+static void index_searchwild__all(struct index_node_f *node, int j,
+ struct strbuf *buf,
+ const char *subkey,
+ struct index_value **out)
+{
+ int pushed = 0;
+ int ch;
+
+ while (node->prefix[j]) {
+ ch = node->prefix[j];
+
+ strbuf_pushchar(buf, ch);
+ pushed++;
+ j++;
+ }
+
+ for (ch = node->first; ch <= node->last; ch++) {
+ struct index_node_f *child = index_readchild(node, ch);
+
+ if (!child)
+ continue;
+
+ strbuf_pushchar(buf, ch);
+ index_searchwild__all(child, 0, buf, subkey, out);
+ strbuf_popchar(buf);
+ }
+
+ if (node->values) {
+ if (fnmatch(strbuf_str(buf), subkey, 0) == 0)
+ index_searchwild__allvalues(node, out);
+ else
+ index_close(node);
+ } else {
+ index_close(node);
+ }
+
+ strbuf_popchars(buf, pushed);
+}
+
+/* Level 2: descend the tree (until we hit a wildcard) */
+static void index_searchwild__node(struct index_node_f *node,
+ struct strbuf *buf,
+ const char *key, int i,
+ struct index_value **out)
+{
+ struct index_node_f *child;
+ int j;
+ int ch;
+
+ while(node) {
+ for (j = 0; node->prefix[j]; j++) {
+ ch = node->prefix[j];
+
+ if (ch == '*' || ch == '?' || ch == '[') {
+ index_searchwild__all(node, j, buf,
+ &key[i+j], out);
+ return;
+ }
+
+ if (ch != key[i+j]) {
+ index_close(node);
+ return;
+ }
+ }
+
+ i += j;
+
+ child = index_readchild(node, '*');
+ if (child) {
+ strbuf_pushchar(buf, '*');
+ index_searchwild__all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ child = index_readchild(node, '?');
+ if (child) {
+ strbuf_pushchar(buf, '?');
+ index_searchwild__all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ child = index_readchild(node, '[');
+ if (child) {
+ strbuf_pushchar(buf, '[');
+ index_searchwild__all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ if (key[i] == '\0') {
+ index_searchwild__allvalues(node, out);
+
+ return;
+ }
+
+ child = index_readchild(node, key[i]);
+ index_close(node);
+ node = child;
+ i++;
+ }
+}
+
+/*
+ * Search the index for a key. The index may contain wildcards.
+ *
+ * Returns a list of all the values of matching keys.
+ */
+struct index_value *index_searchwild(struct index_file *in, const char *key)
+{
+ struct index_node_f *root = index_readroot(in);
+ struct strbuf buf;
+ struct index_value *out = NULL;
+
+ strbuf_init(&buf);
+ index_searchwild__node(root, &buf, key, 0, &out);
+ strbuf_release(&buf);
+ return out;
+}
+
+/**************************************************************************/
+/*
+ * Alternative implementation, using mmap to map all the file to memory when
+ * starting
+ */
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static const char _idx_empty_str[] = "";
+
+struct index_mm {
+ const struct kmod_ctx *ctx;
+ void *mm;
+ uint32_t root_offset;
+ size_t size;
+};
+
+struct index_mm_value {
+ unsigned int priority;
+ unsigned int len;
+ const char *value;
+};
+
+struct index_mm_value_array {
+ struct index_mm_value *values;
+ unsigned int len;
+};
+
+struct index_mm_node {
+ struct index_mm *idx;
+ const char *prefix; /* mmape'd value */
+ struct index_mm_value_array values;
+ unsigned char first;
+ unsigned char last;
+ uint32_t children[];
+};
+
+static inline uint32_t read_long_mm(void **p)
+{
+ uint8_t *addr = *(uint8_t **)p;
+ uint32_t v;
+
+ /* addr may be unalined to uint32_t */
+ v = get_unaligned((uint32_t *) addr);
+
+ *p = addr + sizeof(uint32_t);
+ return ntohl(v);
+}
+
+static inline uint8_t read_char_mm(void **p)
+{
+ uint8_t *addr = *(uint8_t **)p;
+ uint8_t v = *addr;
+ *p = addr + sizeof(uint8_t);
+ return v;
+}
+
+static inline char *read_chars_mm(void **p, unsigned *rlen)
+{
+ char *addr = *(char **)p;
+ size_t len = *rlen = strlen(addr);
+ *p = addr + len + 1;
+ return addr;
+}
+
+static struct index_mm_node *index_mm_read_node(struct index_mm *idx,
+ uint32_t offset) {
+ void *p = idx->mm;
+ struct index_mm_node *node;
+ const char *prefix;
+ int i, child_count, value_count, children_padding;
+ uint32_t children[INDEX_CHILDMAX];
+ char first, last;
+
+ if ((offset & INDEX_NODE_MASK) == 0)
+ return NULL;
+
+ p = (char *)p + (offset & INDEX_NODE_MASK);
+
+ if (offset & INDEX_NODE_PREFIX) {
+ unsigned len;
+ prefix = read_chars_mm(&p, &len);
+ } else
+ prefix = _idx_empty_str;
+
+ if (offset & INDEX_NODE_CHILDS) {
+ first = read_char_mm(&p);
+ last = read_char_mm(&p);
+ child_count = last - first + 1;
+ for (i = 0; i < child_count; i++)
+ children[i] = read_long_mm(&p);
+ } else {
+ first = INDEX_CHILDMAX;
+ last = 0;
+ child_count = 0;
+ }
+
+ children_padding = (sizeof(struct index_mm_node) +
+ (sizeof(uint32_t) * child_count)) % sizeof(void *);
+
+ if (offset & INDEX_NODE_VALUES)
+ value_count = read_long_mm(&p);
+ else
+ value_count = 0;
+
+ node = malloc(sizeof(struct index_mm_node)
+ + sizeof(uint32_t) * child_count + children_padding
+ + sizeof(struct index_mm_value) * value_count);
+ if (node == NULL)
+ return NULL;
+
+ node->idx = idx;
+ node->prefix = prefix;
+ if (value_count == 0)
+ node->values.values = NULL;
+ else {
+ node->values.values = (struct index_mm_value *)
+ ((char *)node + sizeof(struct index_mm_node) +
+ sizeof(uint32_t) * child_count + children_padding);
+ }
+ node->values.len = value_count;
+ node->first = first;
+ node->last = last;
+ memcpy(node->children, children, sizeof(uint32_t) * child_count);
+
+ for (i = 0; i < value_count; i++) {
+ struct index_mm_value *v = node->values.values + i;
+ v->priority = read_long_mm(&p);
+ v->value = read_chars_mm(&p, &v->len);
+ }
+
+ return node;
+}
+
+static void index_mm_free_node(struct index_mm_node *node)
+{
+ free(node);
+}
+
+int index_mm_open(const struct kmod_ctx *ctx, const char *filename,
+ unsigned long long *stamp, struct index_mm **pidx)
+{
+ int fd, err;
+ struct stat st;
+ struct index_mm *idx;
+ struct {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t root_offset;
+ } hdr;
+ void *p;
+
+ assert(pidx != NULL);
+
+ DBG(ctx, "file=%s\n", filename);
+
+ idx = malloc(sizeof(*idx));
+ if (idx == NULL) {
+ ERR(ctx, "malloc: %m\n");
+ return -ENOMEM;
+ }
+
+ if ((fd = open(filename, O_RDONLY|O_CLOEXEC)) < 0) {
+ DBG(ctx, "open(%s, O_RDONLY|O_CLOEXEC): %m\n", filename);
+ err = -errno;
+ goto fail_open;
+ }
+
+ if (fstat(fd, &st) < 0 || (size_t) st.st_size < sizeof(hdr)) {
+ err = -EINVAL;
+ goto fail_nommap;
+ }
+
+ idx->mm = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (idx->mm == MAP_FAILED) {
+ ERR(ctx, "mmap(NULL, %"PRIu64", PROT_READ, %d, MAP_PRIVATE, 0): %m\n",
+ st.st_size, fd);
+ err = -errno;
+ goto fail_nommap;
+ }
+
+ p = idx->mm;
+ hdr.magic = read_long_mm(&p);
+ hdr.version = read_long_mm(&p);
+ hdr.root_offset = read_long_mm(&p);
+
+ if (hdr.magic != INDEX_MAGIC) {
+ ERR(ctx, "magic check fail: %x instead of %x\n", hdr.magic,
+ INDEX_MAGIC);
+ err = -EINVAL;
+ goto fail;
+ }
+
+ if (hdr.version >> 16 != INDEX_VERSION_MAJOR) {
+ ERR(ctx, "major version check fail: %u instead of %u\n",
+ hdr.version >> 16, INDEX_VERSION_MAJOR);
+ err = -EINVAL;
+ goto fail;
+ }
+
+ idx->root_offset = hdr.root_offset;
+ idx->size = st.st_size;
+ idx->ctx = ctx;
+ close(fd);
+
+ *stamp = stat_mstamp(&st);
+ *pidx = idx;
+
+ return 0;
+
+fail:
+ munmap(idx->mm, st.st_size);
+fail_nommap:
+ close(fd);
+fail_open:
+ free(idx);
+ return err;
+}
+
+void index_mm_close(struct index_mm *idx)
+{
+ munmap(idx->mm, idx->size);
+ free(idx);
+}
+
+static struct index_mm_node *index_mm_readroot(struct index_mm *idx)
+{
+ return index_mm_read_node(idx, idx->root_offset);
+}
+
+static struct index_mm_node *index_mm_readchild(const struct index_mm_node *parent,
+ int ch)
+{
+ if (parent->first <= ch && ch <= parent->last) {
+ return index_mm_read_node(parent->idx,
+ parent->children[ch - parent->first]);
+ }
+
+ return NULL;
+}
+
+static void index_mm_dump_node(struct index_mm_node *node, struct strbuf *buf,
+ int fd)
+{
+ struct index_mm_value *itr, *itr_end;
+ int ch, pushed;
+
+ pushed = strbuf_pushchars(buf, node->prefix);
+
+ itr = node->values.values;
+ itr_end = itr + node->values.len;
+ for (; itr < itr_end; itr++) {
+ write_str_safe(fd, buf->bytes, buf->used);
+ write_str_safe(fd, " ", 1);
+ write_str_safe(fd, itr->value, itr->len);
+ write_str_safe(fd, "\n", 1);
+ }
+
+ for (ch = node->first; ch <= node->last; ch++) {
+ struct index_mm_node *child = index_mm_readchild(node, ch);
+
+ if (child == NULL)
+ continue;
+
+ strbuf_pushchar(buf, ch);
+ index_mm_dump_node(child, buf, fd);
+ strbuf_popchar(buf);
+ }
+
+ strbuf_popchars(buf, pushed);
+ index_mm_free_node(node);
+}
+
+void index_mm_dump(struct index_mm *idx, int fd, const char *prefix)
+{
+ struct index_mm_node *root;
+ struct strbuf buf;
+
+ root = index_mm_readroot(idx);
+ if (root == NULL)
+ return;
+
+ strbuf_init(&buf);
+ strbuf_pushchars(&buf, prefix);
+ index_mm_dump_node(root, &buf, fd);
+ strbuf_release(&buf);
+}
+
+static char *index_mm_search_node(struct index_mm_node *node, const char *key,
+ int i)
+{
+ char *value;
+ struct index_mm_node *child;
+ int ch;
+ int j;
+
+ while(node) {
+ for (j = 0; node->prefix[j]; j++) {
+ ch = node->prefix[j];
+
+ if (ch != key[i+j]) {
+ index_mm_free_node(node);
+ return NULL;
+ }
+ }
+
+ i += j;
+
+ if (key[i] == '\0') {
+ value = node->values.len > 0
+ ? strdup(node->values.values[0].value)
+ : NULL;
+
+ index_mm_free_node(node);
+ return value;
+ }
+
+ child = index_mm_readchild(node, key[i]);
+ index_mm_free_node(node);
+ node = child;
+ i++;
+ }
+
+ return NULL;
+}
+
+/*
+ * Search the index for a key
+ *
+ * Returns the value of the first match
+ *
+ * The recursive functions free their node argument (using index_close).
+ */
+char *index_mm_search(struct index_mm *idx, const char *key)
+{
+// FIXME: return value by reference instead of strdup
+ struct index_mm_node *root;
+ char *value;
+
+ root = index_mm_readroot(idx);
+ value = index_mm_search_node(root, key, 0);
+
+ return value;
+}
+
+/* Level 4: add all the values from a matching node */
+static void index_mm_searchwild_allvalues(struct index_mm_node *node,
+ struct index_value **out)
+{
+ struct index_mm_value *itr, *itr_end;
+
+ itr = node->values.values;
+ itr_end = itr + node->values.len;
+ for (; itr < itr_end; itr++)
+ add_value(out, itr->value, itr->len, itr->priority);
+
+ index_mm_free_node(node);
+}
+
+/*
+ * Level 3: traverse a sub-keyspace which starts with a wildcard,
+ * looking for matches.
+ */
+static void index_mm_searchwild_all(struct index_mm_node *node, int j,
+ struct strbuf *buf,
+ const char *subkey,
+ struct index_value **out)
+{
+ int pushed = 0;
+ int ch;
+
+ while (node->prefix[j]) {
+ ch = node->prefix[j];
+
+ strbuf_pushchar(buf, ch);
+ pushed++;
+ j++;
+ }
+
+ for (ch = node->first; ch <= node->last; ch++) {
+ struct index_mm_node *child = index_mm_readchild(node, ch);
+
+ if (!child)
+ continue;
+
+ strbuf_pushchar(buf, ch);
+ index_mm_searchwild_all(child, 0, buf, subkey, out);
+ strbuf_popchar(buf);
+ }
+
+ if (node->values.len > 0) {
+ if (fnmatch(strbuf_str(buf), subkey, 0) == 0)
+ index_mm_searchwild_allvalues(node, out);
+ else
+ index_mm_free_node(node);
+ } else {
+ index_mm_free_node(node);
+ }
+
+ strbuf_popchars(buf, pushed);
+}
+
+/* Level 2: descend the tree (until we hit a wildcard) */
+static void index_mm_searchwild_node(struct index_mm_node *node,
+ struct strbuf *buf,
+ const char *key, int i,
+ struct index_value **out)
+{
+ struct index_mm_node *child;
+ int j;
+ int ch;
+
+ while(node) {
+ for (j = 0; node->prefix[j]; j++) {
+ ch = node->prefix[j];
+
+ if (ch == '*' || ch == '?' || ch == '[') {
+ index_mm_searchwild_all(node, j, buf,
+ &key[i+j], out);
+ return;
+ }
+
+ if (ch != key[i+j]) {
+ index_mm_free_node(node);
+ return;
+ }
+ }
+
+ i += j;
+
+ child = index_mm_readchild(node, '*');
+ if (child) {
+ strbuf_pushchar(buf, '*');
+ index_mm_searchwild_all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ child = index_mm_readchild(node, '?');
+ if (child) {
+ strbuf_pushchar(buf, '?');
+ index_mm_searchwild_all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ child = index_mm_readchild(node, '[');
+ if (child) {
+ strbuf_pushchar(buf, '[');
+ index_mm_searchwild_all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ if (key[i] == '\0') {
+ index_mm_searchwild_allvalues(node, out);
+
+ return;
+ }
+
+ child = index_mm_readchild(node, key[i]);
+ index_mm_free_node(node);
+ node = child;
+ i++;
+ }
+}
+
+/*
+ * Search the index for a key. The index may contain wildcards.
+ *
+ * Returns a list of all the values of matching keys.
+ */
+struct index_value *index_mm_searchwild(struct index_mm *idx, const char *key)
+{
+ struct index_mm_node *root = index_mm_readroot(idx);
+ struct strbuf buf;
+ struct index_value *out = NULL;
+
+ strbuf_init(&buf);
+ index_mm_searchwild_node(root, &buf, key, 0, &out);
+ strbuf_release(&buf);
+ return out;
+}
diff --git a/libkmod/libkmod-index.h b/libkmod/libkmod-index.h
new file mode 100644
index 0000000..db671b0
--- /dev/null
+++ b/libkmod/libkmod-index.h
@@ -0,0 +1,48 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+struct index_value {
+ struct index_value *next;
+ unsigned int priority;
+ unsigned int len;
+ char value[0];
+};
+
+/* In-memory index (depmod only) */
+struct index_file;
+struct index_file *index_file_open(const char *filename);
+void index_file_close(struct index_file *idx);
+char *index_search(struct index_file *idx, const char *key);
+void index_dump(struct index_file *in, int fd, const char *prefix);
+struct index_value *index_searchwild(struct index_file *idx, const char *key);
+
+void index_values_free(struct index_value *values);
+
+/* Implementation using mmap */
+struct index_mm;
+int index_mm_open(const struct kmod_ctx *ctx, const char *filename,
+ unsigned long long *stamp, struct index_mm **pidx);
+void index_mm_close(struct index_mm *index);
+char *index_mm_search(struct index_mm *idx, const char *key);
+struct index_value *index_mm_searchwild(struct index_mm *idx, const char *key);
+void index_mm_dump(struct index_mm *idx, int fd, const char *prefix);
diff --git a/libkmod/libkmod-internal.h b/libkmod/libkmod-internal.h
new file mode 100644
index 0000000..26a7e28
--- /dev/null
+++ b/libkmod/libkmod-internal.h
@@ -0,0 +1,210 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <limits.h>
+
+#include <shared/macro.h>
+#include <shared/missing.h>
+
+#include "libkmod.h"
+
+static _always_inline_ _printf_format_(2, 3) void
+ kmod_log_null(const struct kmod_ctx *ctx, const char *format, ...) {}
+
+#define kmod_log_cond(ctx, prio, arg...) \
+ do { \
+ if (kmod_get_log_priority(ctx) >= prio) \
+ kmod_log(ctx, prio, __FILE__, __LINE__, __func__, ## arg);\
+ } while (0)
+
+#ifdef ENABLE_LOGGING
+# ifdef ENABLE_DEBUG
+# define DBG(ctx, arg...) kmod_log_cond(ctx, LOG_DEBUG, ## arg)
+# else
+# define DBG(ctx, arg...) kmod_log_null(ctx, ## arg)
+# endif
+# define NOTICE(ctx, arg...) kmod_log_cond(ctx, LOG_NOTICE, ## arg)
+# define INFO(ctx, arg...) kmod_log_cond(ctx, LOG_INFO, ## arg)
+# define ERR(ctx, arg...) kmod_log_cond(ctx, LOG_ERR, ## arg)
+#else
+# define DBG(ctx, arg...) kmod_log_null(ctx, ## arg)
+# define NOTICE(ctx, arg...) kmod_log_null(ctx, ## arg)
+# define INFO(ctx, arg...) kmod_log_null(ctx, ## arg)
+# define ERR(ctx, arg...) kmod_log_null(ctx, ## arg)
+#endif
+
+#define KMOD_EXPORT __attribute__ ((visibility("default")))
+
+#define KCMD_LINE_SIZE 4096
+
+#ifndef HAVE_SECURE_GETENV
+# ifdef HAVE___SECURE_GETENV
+# define secure_getenv __secure_getenv
+# else
+# warning neither secure_getenv nor __secure_getenv is available
+# define secure_getenv getenv
+# endif
+#endif
+
+void kmod_log(const struct kmod_ctx *ctx,
+ int priority, const char *file, int line, const char *fn,
+ const char *format, ...) __attribute__((format(printf, 6, 7))) __attribute__((nonnull(1, 3, 5)));
+
+struct list_node {
+ struct list_node *next, *prev;
+};
+
+struct kmod_list {
+ struct list_node node;
+ void *data;
+};
+
+enum kmod_file_compression_type {
+ KMOD_FILE_COMPRESSION_NONE = 0,
+ KMOD_FILE_COMPRESSION_ZSTD,
+ KMOD_FILE_COMPRESSION_XZ,
+ KMOD_FILE_COMPRESSION_ZLIB,
+};
+
+struct kmod_list *kmod_list_append(struct kmod_list *list, const void *data) _must_check_ __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_prepend(struct kmod_list *list, const void *data) _must_check_ __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_remove(struct kmod_list *list) _must_check_;
+struct kmod_list *kmod_list_remove_data(struct kmod_list *list,
+ const void *data) _must_check_ __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_remove_n_latest(struct kmod_list *list,
+ unsigned int n) _must_check_;
+struct kmod_list *kmod_list_insert_after(struct kmod_list *list, const void *data) __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_insert_before(struct kmod_list *list, const void *data) __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_append_list(struct kmod_list *list1, struct kmod_list *list2) _must_check_;
+
+#undef kmod_list_foreach
+#define kmod_list_foreach(list_entry, first_entry) \
+ for (list_entry = ((first_entry) == NULL) ? NULL : (first_entry); \
+ list_entry != NULL; \
+ list_entry = (list_entry->node.next == &((first_entry)->node)) ? NULL : \
+ container_of(list_entry->node.next, struct kmod_list, node))
+
+#undef kmod_list_foreach_reverse
+#define kmod_list_foreach_reverse(list_entry, first_entry) \
+ for (list_entry = (((first_entry) == NULL) ? NULL : container_of(first_entry->node.prev, struct kmod_list, node)); \
+ list_entry != NULL; \
+ list_entry = ((list_entry == first_entry) ? NULL : \
+ container_of(list_entry->node.prev, struct kmod_list, node)))
+
+/* libkmod.c */
+int kmod_lookup_alias_from_config(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_symbols_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_aliases_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_kernel_builtin_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_builtin_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+bool kmod_lookup_alias_is_builtin(struct kmod_ctx *ctx, const char *name) __attribute__((nonnull(1, 2)));
+int kmod_lookup_alias_from_commands(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+void kmod_set_modules_visited(struct kmod_ctx *ctx, bool visited) __attribute__((nonnull((1))));
+void kmod_set_modules_required(struct kmod_ctx *ctx, bool required) __attribute__((nonnull((1))));
+
+char *kmod_search_moddep(struct kmod_ctx *ctx, const char *name) __attribute__((nonnull(1,2)));
+
+struct kmod_module *kmod_pool_get_module(struct kmod_ctx *ctx, const char *key) __attribute__((nonnull(1,2)));
+void kmod_pool_add_module(struct kmod_ctx *ctx, struct kmod_module *mod, const char *key) __attribute__((nonnull(1, 2, 3)));
+void kmod_pool_del_module(struct kmod_ctx *ctx, struct kmod_module *mod, const char *key) __attribute__((nonnull(1, 2, 3)));
+
+const struct kmod_config *kmod_get_config(const struct kmod_ctx *ctx) __attribute__((nonnull(1)));
+enum kmod_file_compression_type kmod_get_kernel_compression(const struct kmod_ctx *ctx) __attribute__((nonnull(1)));
+
+/* libkmod-config.c */
+struct kmod_config_path {
+ unsigned long long stamp;
+ char path[];
+};
+
+struct kmod_config {
+ struct kmod_ctx *ctx;
+ struct kmod_list *aliases;
+ struct kmod_list *blacklists;
+ struct kmod_list *options;
+ struct kmod_list *remove_commands;
+ struct kmod_list *install_commands;
+ struct kmod_list *softdeps;
+
+ struct kmod_list *paths;
+};
+
+int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **config, const char * const *config_paths) __attribute__((nonnull(1, 2,3)));
+void kmod_config_free(struct kmod_config *config) __attribute__((nonnull(1)));
+const char *kmod_blacklist_get_modname(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_alias_get_name(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_alias_get_modname(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_option_get_options(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_option_get_modname(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_command_get_command(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_command_get_modname(const struct kmod_list *l) __attribute__((nonnull(1)));
+
+const char *kmod_softdep_get_name(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) __attribute__((nonnull(1, 2)));
+const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count);
+
+
+/* libkmod-module.c */
+int kmod_module_new_from_alias(struct kmod_ctx *ctx, const char *alias, const char *name, struct kmod_module **mod);
+int kmod_module_parse_depline(struct kmod_module *mod, char *line) __attribute__((nonnull(1, 2)));
+void kmod_module_set_install_commands(struct kmod_module *mod, const char *cmd) __attribute__((nonnull(1)));
+void kmod_module_set_remove_commands(struct kmod_module *mod, const char *cmd) __attribute__((nonnull(1)));
+void kmod_module_set_visited(struct kmod_module *mod, bool visited) __attribute__((nonnull(1)));
+void kmod_module_set_builtin(struct kmod_module *mod, bool builtin) __attribute__((nonnull((1))));
+void kmod_module_set_required(struct kmod_module *mod, bool required) __attribute__((nonnull(1)));
+bool kmod_module_is_builtin(struct kmod_module *mod) __attribute__((nonnull(1)));
+
+/* libkmod-file.c */
+struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx, const char *filename) _must_check_ __attribute__((nonnull(1,2)));
+struct kmod_elf *kmod_file_get_elf(struct kmod_file *file) __attribute__((nonnull(1)));
+void kmod_file_load_contents(struct kmod_file *file) __attribute__((nonnull(1)));
+void *kmod_file_get_contents(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+off_t kmod_file_get_size(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+int kmod_file_get_fd(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+void kmod_file_unref(struct kmod_file *file) __attribute__((nonnull(1)));
+
+/* libkmod-elf.c */
+struct kmod_elf;
+struct kmod_modversion {
+ uint64_t crc;
+ enum kmod_symbol_bind bind;
+ char *symbol;
+};
+
+struct kmod_elf *kmod_elf_new(const void *memory, off_t size) _must_check_;
+void kmod_elf_unref(struct kmod_elf *elf) __attribute__((nonnull(1)));
+const void *kmod_elf_get_memory(const struct kmod_elf *elf) _must_check_ __attribute__((nonnull(1)));
+int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array) _must_check_ __attribute__((nonnull(1,2,3)));
+int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array) _must_check_ __attribute__((nonnull(1,2)));
+int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) _must_check_ __attribute__((nonnull(1,2)));
+int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) _must_check_ __attribute__((nonnull(1,2)));
+int kmod_elf_strip_section(struct kmod_elf *elf, const char *section) _must_check_ __attribute__((nonnull(1,2)));
+int kmod_elf_strip_vermagic(struct kmod_elf *elf) _must_check_ __attribute__((nonnull(1)));
+
+/*
+ * Debug mock lib need to find section ".gnu.linkonce.this_module" in order to
+ * get modname
+ */
+int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, const void **buf, uint64_t *buf_size) _must_check_ __attribute__((nonnull(1,2,3,4)));
+
+/* libkmod-signature.c */
+struct kmod_signature_info {
+ const char *signer;
+ size_t signer_len;
+ const char *key_id;
+ size_t key_id_len;
+ const char *algo, *hash_algo, *id_type;
+ const char *sig;
+ size_t sig_len;
+ void (*free)(void *);
+ void *private;
+};
+bool kmod_module_signature_info(const struct kmod_file *file, struct kmod_signature_info *sig_info) _must_check_ __attribute__((nonnull(1, 2)));
+void kmod_module_signature_info_free(struct kmod_signature_info *sig_info) __attribute__((nonnull));
+
+/* libkmod-builtin.c */
+ssize_t kmod_builtin_get_modinfo(struct kmod_ctx *ctx, const char *modname, char ***modinfo) __attribute__((nonnull(1, 2, 3)));
diff --git a/libkmod/libkmod-list.c b/libkmod/libkmod-list.c
new file mode 100644
index 0000000..7623693
--- /dev/null
+++ b/libkmod/libkmod-list.c
@@ -0,0 +1,314 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+/**
+ * SECTION:libkmod-list
+ * @short_description: general purpose list
+ */
+
+static inline struct list_node *list_node_init(struct list_node *node)
+{
+ node->next = node;
+ node->prev = node;
+
+ return node;
+}
+
+static inline void list_node_append(struct list_node *list,
+ struct list_node *node)
+{
+ if (list == NULL) {
+ list_node_init(node);
+ return;
+ }
+
+ node->prev = list->prev;
+ list->prev->next = node;
+ list->prev = node;
+ node->next = list;
+}
+
+static inline struct list_node *list_node_remove(struct list_node *node)
+{
+ if (node->prev == node || node->next == node)
+ return NULL;
+
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+
+ return node->next;
+}
+
+static inline void list_node_insert_after(struct list_node *list,
+ struct list_node *node)
+{
+ if (list == NULL) {
+ list_node_init(node);
+ return;
+ }
+
+ node->prev = list;
+ node->next = list->next;
+ list->next->prev = node;
+ list->next = node;
+}
+
+static inline void list_node_insert_before(struct list_node *list,
+ struct list_node *node)
+{
+ if (list == NULL) {
+ list_node_init(node);
+ return;
+ }
+
+ node->next = list;
+ node->prev = list->prev;
+ list->prev->next = node;
+ list->prev = node;
+}
+
+static inline void list_node_append_list(struct list_node *list1,
+ struct list_node *list2)
+{
+ struct list_node *list1_last;
+
+ if (list1 == NULL) {
+ list_node_init(list2);
+ return;
+ }
+
+ list1->prev->next = list2;
+ list2->prev->next = list1;
+
+ /* cache the last, because we will lose the pointer */
+ list1_last = list1->prev;
+
+ list1->prev = list2->prev;
+ list2->prev = list1_last;
+}
+
+struct kmod_list *kmod_list_append(struct kmod_list *list, const void *data)
+{
+ struct kmod_list *new;
+
+ new = malloc(sizeof(*new));
+ if (new == NULL)
+ return NULL;
+
+ new->data = (void *)data;
+ list_node_append(list ? &list->node : NULL, &new->node);
+
+ return list ? list : new;
+}
+
+struct kmod_list *kmod_list_insert_after(struct kmod_list *list,
+ const void *data)
+{
+ struct kmod_list *new;
+
+ if (list == NULL)
+ return kmod_list_append(list, data);
+
+ new = malloc(sizeof(*new));
+ if (new == NULL)
+ return NULL;
+
+ new->data = (void *)data;
+ list_node_insert_after(&list->node, &new->node);
+
+ return list;
+}
+
+struct kmod_list *kmod_list_insert_before(struct kmod_list *list,
+ const void *data)
+{
+ struct kmod_list *new;
+
+ if (list == NULL)
+ return kmod_list_append(list, data);
+
+ new = malloc(sizeof(*new));
+ if (new == NULL)
+ return NULL;
+
+ new->data = (void *)data;
+ list_node_insert_before(&list->node, &new->node);
+
+ return new;
+}
+
+struct kmod_list *kmod_list_append_list(struct kmod_list *list1,
+ struct kmod_list *list2)
+{
+ if (list1 == NULL)
+ return list2;
+
+ if (list2 == NULL)
+ return list1;
+
+ list_node_append_list(&list1->node, &list2->node);
+
+ return list1;
+}
+
+struct kmod_list *kmod_list_prepend(struct kmod_list *list, const void *data)
+{
+ struct kmod_list *new;
+
+ new = malloc(sizeof(*new));
+ if (new == NULL)
+ return NULL;
+
+ new->data = (void *)data;
+ list_node_append(list ? &list->node : NULL, &new->node);
+
+ return new;
+}
+
+struct kmod_list *kmod_list_remove(struct kmod_list *list)
+{
+ struct list_node *node;
+
+ if (list == NULL)
+ return NULL;
+
+ node = list_node_remove(&list->node);
+ free(list);
+
+ if (node == NULL)
+ return NULL;
+
+ return container_of(node, struct kmod_list, node);
+}
+
+struct kmod_list *kmod_list_remove_data(struct kmod_list *list,
+ const void *data)
+{
+ struct kmod_list *itr;
+ struct list_node *node;
+
+ for (itr = list; itr != NULL; itr = kmod_list_next(list, itr)) {
+ if (itr->data == data)
+ break;
+ }
+
+ if (itr == NULL)
+ return list;
+
+ node = list_node_remove(&itr->node);
+ free(itr);
+
+ if (node == NULL)
+ return NULL;
+
+ return container_of(node, struct kmod_list, node);
+}
+
+/*
+ * n must be greater to or equal the number of elements (we don't check the
+ * condition)
+ */
+struct kmod_list *kmod_list_remove_n_latest(struct kmod_list *list,
+ unsigned int n)
+{
+ struct kmod_list *l = list;
+ unsigned int i;
+
+ for (i = 0; i < n; i++) {
+ l = kmod_list_last(l);
+ l = kmod_list_remove(l);
+ }
+
+ return l;
+}
+
+/**
+ * kmod_list_prev:
+ * @list: the head of the list
+ * @curr: the current node in the list
+ *
+ * Get the previous node in @list relative to @curr as if @list was not a
+ * circular list. I.e.: the previous of the head is NULL. It can be used to
+ * iterate a list by checking for NULL return to know when all elements were
+ * iterated.
+ *
+ * Returns: node previous to @curr or NULL if either this node is the head of
+ * the list or the list is empty.
+ */
+KMOD_EXPORT struct kmod_list *kmod_list_prev(const struct kmod_list *list,
+ const struct kmod_list *curr)
+{
+ if (list == NULL || curr == NULL)
+ return NULL;
+
+ if (list == curr)
+ return NULL;
+
+ return container_of(curr->node.prev, struct kmod_list, node);
+}
+
+/**
+ * kmod_list_next:
+ * @list: the head of the list
+ * @curr: the current node in the list
+ *
+ * Get the next node in @list relative to @curr as if @list was not a circular
+ * list. I.e. calling this function in the last node of the list returns
+ * NULL.. It can be used to iterate a list by checking for NULL return to know
+ * when all elements were iterated.
+ *
+ * Returns: node next to @curr or NULL if either this node is the last of or
+ * list is empty.
+ */
+KMOD_EXPORT struct kmod_list *kmod_list_next(const struct kmod_list *list,
+ const struct kmod_list *curr)
+{
+ if (list == NULL || curr == NULL)
+ return NULL;
+
+ if (curr->node.next == &list->node)
+ return NULL;
+
+ return container_of(curr->node.next, struct kmod_list, node);
+}
+
+/**
+ * kmod_list_last:
+ * @list: the head of the list
+ *
+ * Get the last element of the @list. As @list is a circular list,
+ * this is a cheap operation O(1) with the last element being the
+ * previous element.
+ *
+ * If the list has a single element it will return the list itself (as
+ * expected, and this is what differentiates from kmod_list_prev()).
+ *
+ * Returns: last node at @list or NULL if the list is empty.
+ */
+KMOD_EXPORT struct kmod_list *kmod_list_last(const struct kmod_list *list)
+{
+ if (list == NULL)
+ return NULL;
+ return container_of(list->node.prev, struct kmod_list, node);
+}
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
new file mode 100644
index 0000000..585da41
--- /dev/null
+++ b/libkmod/libkmod-module.c
@@ -0,0 +1,2986 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifdef HAVE_LINUX_MODULE_H
+#include <linux/module.h>
+#endif
+
+#include <shared/util.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+/**
+ * SECTION:libkmod-module
+ * @short_description: operate on kernel modules
+ */
+
+enum kmod_module_builtin {
+ KMOD_MODULE_BUILTIN_UNKNOWN,
+ KMOD_MODULE_BUILTIN_NO,
+ KMOD_MODULE_BUILTIN_YES,
+};
+
+/**
+ * kmod_module:
+ *
+ * Opaque object representing a module.
+ */
+struct kmod_module {
+ struct kmod_ctx *ctx;
+ char *hashkey;
+ char *name;
+ char *path;
+ struct kmod_list *dep;
+ char *options;
+ const char *install_commands; /* owned by kmod_config */
+ const char *remove_commands; /* owned by kmod_config */
+ char *alias; /* only set if this module was created from an alias */
+ struct kmod_file *file;
+ int n_dep;
+ int refcount;
+ struct {
+ bool dep : 1;
+ bool options : 1;
+ bool install_commands : 1;
+ bool remove_commands : 1;
+ } init;
+
+ /*
+ * mark if module is builtin, i.e. it's present on modules.builtin
+ * file. This is set as soon as it is needed or as soon as we know
+ * about it, i.e. the module was created from builtin lookup.
+ */
+ enum kmod_module_builtin builtin;
+
+ /*
+ * private field used by kmod_module_get_probe_list() to detect
+ * dependency loops
+ */
+ bool visited : 1;
+
+ /*
+ * set by kmod_module_get_probe_list: indicates for probe_insert()
+ * whether the module's command and softdep should be ignored
+ */
+ bool ignorecmd : 1;
+
+ /*
+ * set by kmod_module_get_probe_list: indicates whether this is the
+ * module the user asked for or its dependency, or whether this
+ * is a softdep only
+ */
+ bool required : 1;
+};
+
+static inline const char *path_join(const char *path, size_t prefixlen,
+ char buf[PATH_MAX])
+{
+ size_t pathlen;
+
+ if (path[0] == '/')
+ return path;
+
+ pathlen = strlen(path);
+ if (prefixlen + pathlen + 1 >= PATH_MAX)
+ return NULL;
+
+ memcpy(buf + prefixlen, path, pathlen + 1);
+ return buf;
+}
+
+static inline bool module_is_inkernel(struct kmod_module *mod)
+{
+ int state = kmod_module_get_initstate(mod);
+
+ if (state == KMOD_MODULE_LIVE ||
+ state == KMOD_MODULE_BUILTIN)
+ return true;
+
+ return false;
+}
+
+int kmod_module_parse_depline(struct kmod_module *mod, char *line)
+{
+ struct kmod_ctx *ctx = mod->ctx;
+ struct kmod_list *list = NULL;
+ const char *dirname;
+ char buf[PATH_MAX];
+ char *p, *saveptr;
+ int err = 0, n = 0;
+ size_t dirnamelen;
+
+ if (mod->init.dep)
+ return mod->n_dep;
+ assert(mod->dep == NULL);
+ mod->init.dep = true;
+
+ p = strchr(line, ':');
+ if (p == NULL)
+ return 0;
+
+ *p = '\0';
+ dirname = kmod_get_dirname(mod->ctx);
+ dirnamelen = strlen(dirname);
+ if (dirnamelen + 2 >= PATH_MAX)
+ return 0;
+
+ memcpy(buf, dirname, dirnamelen);
+ buf[dirnamelen] = '/';
+ dirnamelen++;
+ buf[dirnamelen] = '\0';
+
+ if (mod->path == NULL) {
+ const char *str = path_join(line, dirnamelen, buf);
+ if (str == NULL)
+ return 0;
+ mod->path = strdup(str);
+ if (mod->path == NULL)
+ return 0;
+ }
+
+ p++;
+ for (p = strtok_r(p, " \t", &saveptr); p != NULL;
+ p = strtok_r(NULL, " \t", &saveptr)) {
+ struct kmod_module *depmod = NULL;
+ const char *path;
+
+ path = path_join(p, dirnamelen, buf);
+ if (path == NULL) {
+ ERR(ctx, "could not join path '%s' and '%s'.\n",
+ dirname, p);
+ goto fail;
+ }
+
+ err = kmod_module_new_from_path(ctx, path, &depmod);
+ if (err < 0) {
+ ERR(ctx, "ctx=%p path=%s error=%s\n",
+ ctx, path, strerror(-err));
+ goto fail;
+ }
+
+ DBG(ctx, "add dep: %s\n", path);
+
+ list = kmod_list_prepend(list, depmod);
+ n++;
+ }
+
+ DBG(ctx, "%d dependencies for %s\n", n, mod->name);
+
+ mod->dep = list;
+ mod->n_dep = n;
+ return n;
+
+fail:
+ kmod_module_unref_list(list);
+ mod->init.dep = false;
+ return err;
+}
+
+void kmod_module_set_visited(struct kmod_module *mod, bool visited)
+{
+ mod->visited = visited;
+}
+
+void kmod_module_set_builtin(struct kmod_module *mod, bool builtin)
+{
+ mod->builtin =
+ builtin ? KMOD_MODULE_BUILTIN_YES : KMOD_MODULE_BUILTIN_NO;
+}
+
+void kmod_module_set_required(struct kmod_module *mod, bool required)
+{
+ mod->required = required;
+}
+
+bool kmod_module_is_builtin(struct kmod_module *mod)
+{
+ if (mod->builtin == KMOD_MODULE_BUILTIN_UNKNOWN) {
+ kmod_module_set_builtin(mod,
+ kmod_lookup_alias_is_builtin(mod->ctx, mod->name));
+ }
+
+ return mod->builtin == KMOD_MODULE_BUILTIN_YES;
+}
+/*
+ * Memory layout with alias:
+ *
+ * struct kmod_module {
+ * hashkey -----.
+ * alias -----. |
+ * name ----. | |
+ * } | | |
+ * name <----------' | |
+ * alias <-----------' |
+ * name\alias <--------'
+ *
+ * Memory layout without alias:
+ *
+ * struct kmod_module {
+ * hashkey ---.
+ * alias -----|----> NULL
+ * name ----. |
+ * } | |
+ * name <----------'-'
+ *
+ * @key is "name\alias" or "name" (in which case alias == NULL)
+ */
+static int kmod_module_new(struct kmod_ctx *ctx, const char *key,
+ const char *name, size_t namelen,
+ const char *alias, size_t aliaslen,
+ struct kmod_module **mod)
+{
+ struct kmod_module *m;
+ size_t keylen;
+
+ m = kmod_pool_get_module(ctx, key);
+ if (m != NULL) {
+ *mod = kmod_module_ref(m);
+ return 0;
+ }
+
+ if (alias == NULL)
+ keylen = namelen;
+ else
+ keylen = namelen + aliaslen + 1;
+
+ m = malloc(sizeof(*m) + (alias == NULL ? 1 : 2) * (keylen + 1));
+ if (m == NULL)
+ return -ENOMEM;
+
+ memset(m, 0, sizeof(*m));
+
+ m->ctx = kmod_ref(ctx);
+ m->name = (char *)m + sizeof(*m);
+ memcpy(m->name, key, keylen + 1);
+ if (alias == NULL) {
+ m->hashkey = m->name;
+ m->alias = NULL;
+ } else {
+ m->name[namelen] = '\0';
+ m->alias = m->name + namelen + 1;
+ m->hashkey = m->name + keylen + 1;
+ memcpy(m->hashkey, key, keylen + 1);
+ }
+
+ m->refcount = 1;
+ kmod_pool_add_module(ctx, m, m->hashkey);
+ *mod = m;
+
+ return 0;
+}
+
+/**
+ * kmod_module_new_from_name:
+ * @ctx: kmod library context
+ * @name: name of the module
+ * @mod: where to save the created struct kmod_module
+ *
+ * Create a new struct kmod_module using the module name. @name can not be an
+ * alias, file name or anything else; it must be a module name. There's no
+ * check if the module exists in the system.
+ *
+ * This function is also used internally by many others that return a new
+ * struct kmod_module or a new list of modules.
+ *
+ * The initial refcount is 1, and needs to be decremented to release the
+ * resources of the kmod_module. Since libkmod keeps track of all
+ * kmod_modules created, they are all released upon @ctx destruction too. Do
+ * not unref @ctx before all the desired operations with the returned
+ * kmod_module are done.
+ *
+ * Returns: 0 on success or < 0 otherwise. It fails if name is not a valid
+ * module name or if memory allocation failed.
+ */
+KMOD_EXPORT int kmod_module_new_from_name(struct kmod_ctx *ctx,
+ const char *name,
+ struct kmod_module **mod)
+{
+ size_t namelen;
+ char name_norm[PATH_MAX];
+
+ if (ctx == NULL || name == NULL || mod == NULL)
+ return -ENOENT;
+
+ modname_normalize(name, name_norm, &namelen);
+
+ return kmod_module_new(ctx, name_norm, name_norm, namelen, NULL, 0, mod);
+}
+
+int kmod_module_new_from_alias(struct kmod_ctx *ctx, const char *alias,
+ const char *name, struct kmod_module **mod)
+{
+ int err;
+ char key[PATH_MAX];
+ size_t namelen = strlen(name);
+ size_t aliaslen = strlen(alias);
+
+ if (namelen + aliaslen + 2 > PATH_MAX)
+ return -ENAMETOOLONG;
+
+ memcpy(key, name, namelen);
+ memcpy(key + namelen + 1, alias, aliaslen + 1);
+ key[namelen] = '\\';
+
+ err = kmod_module_new(ctx, key, name, namelen, alias, aliaslen, mod);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * kmod_module_new_from_path:
+ * @ctx: kmod library context
+ * @path: path where to find the given module
+ * @mod: where to save the created struct kmod_module
+ *
+ * Create a new struct kmod_module using the module path. @path must be an
+ * existent file with in the filesystem and must be accessible to libkmod.
+ *
+ * The initial refcount is 1, and needs to be decremented to release the
+ * resources of the kmod_module. Since libkmod keeps track of all
+ * kmod_modules created, they are all released upon @ctx destruction too. Do
+ * not unref @ctx before all the desired operations with the returned
+ * kmod_module are done.
+ *
+ * If @path is relative, it's treated as relative to the current working
+ * directory. Otherwise, give an absolute path.
+ *
+ * Returns: 0 on success or < 0 otherwise. It fails if file does not exist, if
+ * it's not a valid file for a kmod_module or if memory allocation failed.
+ */
+KMOD_EXPORT int kmod_module_new_from_path(struct kmod_ctx *ctx,
+ const char *path,
+ struct kmod_module **mod)
+{
+ struct kmod_module *m;
+ int err;
+ struct stat st;
+ char name[PATH_MAX];
+ char *abspath;
+ size_t namelen;
+
+ if (ctx == NULL || path == NULL || mod == NULL)
+ return -ENOENT;
+
+ abspath = path_make_absolute_cwd(path);
+ if (abspath == NULL) {
+ DBG(ctx, "no absolute path for %s\n", path);
+ return -ENOMEM;
+ }
+
+ err = stat(abspath, &st);
+ if (err < 0) {
+ err = -errno;
+ DBG(ctx, "stat %s: %s\n", path, strerror(errno));
+ free(abspath);
+ return err;
+ }
+
+ if (path_to_modname(path, name, &namelen) == NULL) {
+ DBG(ctx, "could not get modname from path %s\n", path);
+ free(abspath);
+ return -ENOENT;
+ }
+
+ m = kmod_pool_get_module(ctx, name);
+ if (m != NULL) {
+ if (m->path == NULL)
+ m->path = abspath;
+ else if (streq(m->path, abspath))
+ free(abspath);
+ else {
+ ERR(ctx, "kmod_module '%s' already exists with different path: new-path='%s' old-path='%s'\n",
+ name, abspath, m->path);
+ free(abspath);
+ return -EEXIST;
+ }
+
+ kmod_module_ref(m);
+ } else {
+ err = kmod_module_new(ctx, name, name, namelen, NULL, 0, &m);
+ if (err < 0) {
+ free(abspath);
+ return err;
+ }
+
+ m->path = abspath;
+ }
+
+ m->builtin = KMOD_MODULE_BUILTIN_NO;
+ *mod = m;
+
+ return 0;
+}
+
+/**
+ * kmod_module_unref:
+ * @mod: kmod module
+ *
+ * Drop a reference of the kmod module. If the refcount reaches zero, its
+ * resources are released.
+ *
+ * Returns: NULL if @mod is NULL or if the module was released. Otherwise it
+ * returns the passed @mod with its refcount decremented.
+ */
+KMOD_EXPORT struct kmod_module *kmod_module_unref(struct kmod_module *mod)
+{
+ if (mod == NULL)
+ return NULL;
+
+ if (--mod->refcount > 0)
+ return mod;
+
+ DBG(mod->ctx, "kmod_module %p released\n", mod);
+
+ kmod_pool_del_module(mod->ctx, mod, mod->hashkey);
+ kmod_module_unref_list(mod->dep);
+
+ if (mod->file)
+ kmod_file_unref(mod->file);
+
+ kmod_unref(mod->ctx);
+ free(mod->options);
+ free(mod->path);
+ free(mod);
+ return NULL;
+}
+
+/**
+ * kmod_module_ref:
+ * @mod: kmod module
+ *
+ * Take a reference of the kmod module, incrementing its refcount.
+ *
+ * Returns: the passed @module with its refcount incremented.
+ */
+KMOD_EXPORT struct kmod_module *kmod_module_ref(struct kmod_module *mod)
+{
+ if (mod == NULL)
+ return NULL;
+
+ mod->refcount++;
+
+ return mod;
+}
+
+typedef int (*lookup_func)(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+
+static int __kmod_module_new_from_lookup(struct kmod_ctx *ctx, const lookup_func lookup[],
+ size_t lookup_count, const char *s,
+ struct kmod_list **list)
+{
+ unsigned int i;
+
+ for (i = 0; i < lookup_count; i++) {
+ int err;
+
+ err = lookup[i](ctx, s, list);
+ if (err < 0 && err != -ENOSYS)
+ return err;
+ else if (*list != NULL)
+ return 0;
+ }
+
+ return 0;
+}
+
+/**
+ * kmod_module_new_from_lookup:
+ * @ctx: kmod library context
+ * @given_alias: alias to look for
+ * @list: an empty list where to save the list of modules matching
+ * @given_alias
+ *
+ * Create a new list of kmod modules using an alias or module name and lookup
+ * libkmod's configuration files and indexes in order to find the module.
+ * Once it's found in one of the places, it stops searching and create the
+ * list of modules that is saved in @list.
+ *
+ * The search order is: 1. aliases in configuration file; 2. module names in
+ * modules.dep index; 3. symbol aliases in modules.symbols index; 4. aliases
+ * from install commands; 5. builtin indexes from kernel.
+ *
+ * The initial refcount is 1, and needs to be decremented to release the
+ * resources of the kmod_module. The returned @list must be released by
+ * calling kmod_module_unref_list(). Since libkmod keeps track of all
+ * kmod_modules created, they are all released upon @ctx destruction too. Do
+ * not unref @ctx before all the desired operations with the returned list are
+ * completed.
+ *
+ * Returns: 0 on success or < 0 otherwise. It fails if any of the lookup
+ * methods failed, which is basically due to memory allocation fail. If module
+ * is not found, it still returns 0, but @list is an empty list.
+ */
+KMOD_EXPORT int kmod_module_new_from_lookup(struct kmod_ctx *ctx,
+ const char *given_alias,
+ struct kmod_list **list)
+{
+ static const lookup_func lookup[] = {
+ kmod_lookup_alias_from_config,
+ kmod_lookup_alias_from_moddep_file,
+ kmod_lookup_alias_from_symbols_file,
+ kmod_lookup_alias_from_commands,
+ kmod_lookup_alias_from_aliases_file,
+ kmod_lookup_alias_from_builtin_file,
+ kmod_lookup_alias_from_kernel_builtin_file,
+ };
+ char alias[PATH_MAX];
+ int err;
+
+ if (ctx == NULL || given_alias == NULL)
+ return -ENOENT;
+
+ if (list == NULL || *list != NULL) {
+ ERR(ctx, "An empty list is needed to create lookup\n");
+ return -ENOSYS;
+ }
+
+ if (alias_normalize(given_alias, alias, NULL) < 0) {
+ DBG(ctx, "invalid alias: %s\n", given_alias);
+ return -EINVAL;
+ }
+
+ DBG(ctx, "input alias=%s, normalized=%s\n", given_alias, alias);
+
+ err = __kmod_module_new_from_lookup(ctx, lookup, ARRAY_SIZE(lookup),
+ alias, list);
+
+ DBG(ctx, "lookup=%s found=%d\n", alias, err >= 0 && *list);
+
+ if (err < 0) {
+ kmod_module_unref_list(*list);
+ *list = NULL;
+ }
+
+ return err;
+}
+
+/**
+ * kmod_module_new_from_name_lookup:
+ * @ctx: kmod library context
+ * @modname: module name to look for
+ * @mod: returned module on success
+ *
+ * Lookup by module name, without considering possible aliases. This is similar
+ * to kmod_module_new_from_lookup(), but don't consider as source indexes and
+ * configurations that work with aliases. When succesful, this always resolves
+ * to one and only one module.
+ *
+ * The search order is: 1. module names in modules.dep index;
+ * 2. builtin indexes from kernel.
+ *
+ * The initial refcount is 1, and needs to be decremented to release the
+ * resources of the kmod_module. Since libkmod keeps track of all
+ * kmod_modules created, they are all released upon @ctx destruction too. Do
+ * not unref @ctx before all the desired operations with the returned list are
+ * completed.
+ *
+ * Returns: 0 on success or < 0 otherwise. It fails if any of the lookup
+ * methods failed, which is basically due to memory allocation failure. If
+ * module is not found, it still returns 0, but @mod is left untouched.
+ */
+KMOD_EXPORT int kmod_module_new_from_name_lookup(struct kmod_ctx *ctx,
+ const char *modname,
+ struct kmod_module **mod)
+{
+ static const lookup_func lookup[] = {
+ kmod_lookup_alias_from_moddep_file,
+ kmod_lookup_alias_from_builtin_file,
+ kmod_lookup_alias_from_kernel_builtin_file,
+ };
+ char name_norm[PATH_MAX];
+ struct kmod_list *list = NULL;
+ int err;
+
+ if (ctx == NULL || modname == NULL || mod == NULL)
+ return -ENOENT;
+
+ modname_normalize(modname, name_norm, NULL);
+
+ DBG(ctx, "input modname=%s, normalized=%s\n", modname, name_norm);
+
+ err = __kmod_module_new_from_lookup(ctx, lookup, ARRAY_SIZE(lookup),
+ name_norm, &list);
+
+ DBG(ctx, "lookup=%s found=%d\n", name_norm, err >= 0 && list);
+
+ if (err >= 0 && list != NULL)
+ *mod = kmod_module_get_module(list);
+
+ kmod_module_unref_list(list);
+
+ return err;
+}
+
+/**
+ * kmod_module_unref_list:
+ * @list: list of kmod modules
+ *
+ * Drop a reference of each kmod module in @list and releases the resources
+ * taken by the list itself.
+ *
+ * Returns: 0
+ */
+KMOD_EXPORT int kmod_module_unref_list(struct kmod_list *list)
+{
+ for (; list != NULL; list = kmod_list_remove(list))
+ kmod_module_unref(list->data);
+
+ return 0;
+}
+
+/**
+ * kmod_module_get_filtered_blacklist:
+ * @ctx: kmod library context
+ * @input: list of kmod_module to be filtered with blacklist
+ * @output: where to save the new list
+ *
+ * This function should not be used. Use kmod_module_apply_filter instead.
+ *
+ * Given a list @input, this function filter it out with config's blacklist
+ * and save it in @output.
+ *
+ * Returns: 0 on success or < 0 otherwise. @output is saved with the updated
+ * list.
+ */
+KMOD_EXPORT int kmod_module_get_filtered_blacklist(const struct kmod_ctx *ctx,
+ const struct kmod_list *input,
+ struct kmod_list **output)
+{
+ return kmod_module_apply_filter(ctx, KMOD_FILTER_BLACKLIST, input, output);
+}
+
+static const struct kmod_list *module_get_dependencies_noref(const struct kmod_module *mod)
+{
+ if (!mod->init.dep) {
+ /* lazy init */
+ char *line = kmod_search_moddep(mod->ctx, mod->name);
+
+ if (line == NULL)
+ return NULL;
+
+ kmod_module_parse_depline((struct kmod_module *)mod, line);
+ free(line);
+
+ if (!mod->init.dep)
+ return NULL;
+ }
+
+ return mod->dep;
+}
+
+/**
+ * kmod_module_get_dependencies:
+ * @mod: kmod module
+ *
+ * Search the modules.dep index to find the dependencies of the given @mod.
+ * The result is cached in @mod, so subsequent calls to this function will
+ * return the already searched list of modules.
+ *
+ * Returns: NULL on failure. Otherwise it returns a list of kmod modules
+ * that can be released by calling kmod_module_unref_list().
+ */
+KMOD_EXPORT struct kmod_list *kmod_module_get_dependencies(const struct kmod_module *mod)
+{
+ struct kmod_list *l, *l_new, *list_new = NULL;
+
+ if (mod == NULL)
+ return NULL;
+
+ module_get_dependencies_noref(mod);
+
+ kmod_list_foreach(l, mod->dep) {
+ l_new = kmod_list_append(list_new, kmod_module_ref(l->data));
+ if (l_new == NULL) {
+ kmod_module_unref(l->data);
+ goto fail;
+ }
+
+ list_new = l_new;
+ }
+
+ return list_new;
+
+fail:
+ ERR(mod->ctx, "out of memory\n");
+ kmod_module_unref_list(list_new);
+ return NULL;
+}
+
+/**
+ * kmod_module_get_module:
+ * @entry: an entry in a list of kmod modules.
+ *
+ * Get the kmod module of this @entry in the list, increasing its refcount.
+ * After it's used, unref it. Since the refcount is incremented upon return,
+ * you still have to call kmod_module_unref_list() to release the list of kmod
+ * modules.
+ *
+ * Returns: NULL on failure or the kmod_module contained in this list entry
+ * with its refcount incremented.
+ */
+KMOD_EXPORT struct kmod_module *kmod_module_get_module(const struct kmod_list *entry)
+{
+ if (entry == NULL)
+ return NULL;
+
+ return kmod_module_ref(entry->data);
+}
+
+/**
+ * kmod_module_get_name:
+ * @mod: kmod module
+ *
+ * Get the name of this kmod module. Name is always available, independently
+ * if it was created by kmod_module_new_from_name() or another function and
+ * it's always normalized (dashes are replaced with underscores).
+ *
+ * Returns: the name of this kmod module.
+ */
+KMOD_EXPORT const char *kmod_module_get_name(const struct kmod_module *mod)
+{
+ if (mod == NULL)
+ return NULL;
+
+ return mod->name;
+}
+
+/**
+ * kmod_module_get_path:
+ * @mod: kmod module
+ *
+ * Get the path of this kmod module. If this kmod module was not created by
+ * path, it can search the modules.dep index in order to find out the module
+ * under context's dirname.
+ *
+ * Returns: the path of this kmod module or NULL if such information is not
+ * available.
+ */
+KMOD_EXPORT const char *kmod_module_get_path(const struct kmod_module *mod)
+{
+ char *line;
+
+ if (mod == NULL)
+ return NULL;
+
+ DBG(mod->ctx, "name='%s' path='%s'\n", mod->name, mod->path);
+
+ if (mod->path != NULL)
+ return mod->path;
+ if (mod->init.dep)
+ return NULL;
+
+ /* lazy init */
+ line = kmod_search_moddep(mod->ctx, mod->name);
+ if (line == NULL)
+ return NULL;
+
+ kmod_module_parse_depline((struct kmod_module *) mod, line);
+ free(line);
+
+ return mod->path;
+}
+
+
+extern long delete_module(const char *name, unsigned int flags);
+
+/**
+ * kmod_module_remove_module:
+ * @mod: kmod module
+ * @flags: flags used when removing the module.
+ * KMOD_REMOVE_FORCE: force remove module regardless if it's still in
+ * use by a kernel subsystem or other process; passed directly to Linux kernel
+ * KMOD_REMOVE_NOWAIT: is always enforced, causing us to pass O_NONBLOCK to
+ * delete_module(2).
+ * KMOD_REMOVE_NOLOG: when module removal fails, do not log anything as the
+ * caller may want to handle retries and log when appropriate.
+ *
+ * Remove a module from Linux kernel.
+ *
+ * Returns: 0 on success or < 0 on failure.
+ */
+KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod,
+ unsigned int flags)
+{
+ unsigned int libkmod_flags = flags & 0xff;
+
+ int err;
+
+ if (mod == NULL)
+ return -ENOENT;
+
+ /* Filter out other flags and force ONONBLOCK */
+ flags &= KMOD_REMOVE_FORCE;
+ flags |= KMOD_REMOVE_NOWAIT;
+
+ err = delete_module(mod->name, flags);
+ if (err != 0) {
+ err = -errno;
+ if (!(libkmod_flags & KMOD_REMOVE_NOLOG))
+ ERR(mod->ctx, "could not remove '%s': %m\n", mod->name);
+ }
+
+ return err;
+}
+
+extern long init_module(const void *mem, unsigned long len, const char *args);
+
+static int do_finit_module(struct kmod_module *mod, unsigned int flags,
+ const char *args)
+{
+ enum kmod_file_compression_type compression, kernel_compression;
+ unsigned int kernel_flags = 0;
+ int err;
+
+ /*
+ * When module is not compressed or its compression type matches the
+ * one in use by the kernel, there is no need to read the file
+ * in userspace. Otherwise, re-use ENOSYS to trigger the same fallback
+ * as when finit_module() is not supported.
+ */
+ compression = kmod_file_get_compression(mod->file);
+ kernel_compression = kmod_get_kernel_compression(mod->ctx);
+ if (!(compression == KMOD_FILE_COMPRESSION_NONE ||
+ compression == kernel_compression))
+ return -ENOSYS;
+
+ if (compression != KMOD_FILE_COMPRESSION_NONE)
+ kernel_flags |= MODULE_INIT_COMPRESSED_FILE;
+
+ if (flags & KMOD_INSERT_FORCE_VERMAGIC)
+ kernel_flags |= MODULE_INIT_IGNORE_VERMAGIC;
+ if (flags & KMOD_INSERT_FORCE_MODVERSION)
+ kernel_flags |= MODULE_INIT_IGNORE_MODVERSIONS;
+
+ err = finit_module(kmod_file_get_fd(mod->file), args, kernel_flags);
+ if (err < 0)
+ err = -errno;
+
+ return err;
+}
+
+static int do_init_module(struct kmod_module *mod, unsigned int flags,
+ const char *args)
+{
+ struct kmod_elf *elf;
+ const void *mem;
+ off_t size;
+ int err;
+
+ kmod_file_load_contents(mod->file);
+
+ if (flags & (KMOD_INSERT_FORCE_VERMAGIC | KMOD_INSERT_FORCE_MODVERSION)) {
+ elf = kmod_file_get_elf(mod->file);
+ if (elf == NULL) {
+ err = -errno;
+ return err;
+ }
+
+ if (flags & KMOD_INSERT_FORCE_MODVERSION) {
+ err = kmod_elf_strip_section(elf, "__versions");
+ if (err < 0)
+ INFO(mod->ctx, "Failed to strip modversion: %s\n", strerror(-err));
+ }
+
+ if (flags & KMOD_INSERT_FORCE_VERMAGIC) {
+ err = kmod_elf_strip_vermagic(elf);
+ if (err < 0)
+ INFO(mod->ctx, "Failed to strip vermagic: %s\n", strerror(-err));
+ }
+
+ mem = kmod_elf_get_memory(elf);
+ } else {
+ mem = kmod_file_get_contents(mod->file);
+ }
+ size = kmod_file_get_size(mod->file);
+
+ err = init_module(mem, size, args);
+ if (err < 0)
+ err = -errno;
+
+ return err;
+}
+
+/**
+ * kmod_module_insert_module:
+ * @mod: kmod module
+ * @flags: flags are not passed to Linux Kernel, but instead they dictate the
+ * behavior of this function, valid flags are
+ * KMOD_INSERT_FORCE_VERMAGIC: ignore kernel version magic;
+ * KMOD_INSERT_FORCE_MODVERSION: ignore symbol version hashes.
+ * @options: module's options to pass to Linux Kernel.
+ *
+ * Insert a module in Linux kernel. It opens the file pointed by @mod,
+ * mmap'ing it and passing to kernel.
+ *
+ * Returns: 0 on success or < 0 on failure. If module is already loaded it
+ * returns -EEXIST.
+ */
+KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
+ unsigned int flags,
+ const char *options)
+{
+ int err;
+ const char *path;
+ const char *args = options ? options : "";
+
+ if (mod == NULL)
+ return -ENOENT;
+
+ path = kmod_module_get_path(mod);
+ if (path == NULL) {
+ ERR(mod->ctx, "could not find module by name='%s'\n", mod->name);
+ return -ENOENT;
+ }
+
+ if (!mod->file) {
+ mod->file = kmod_file_open(mod->ctx, path);
+ if (mod->file == NULL) {
+ err = -errno;
+ return err;
+ }
+ }
+
+ err = do_finit_module(mod, flags, args);
+ if (err == -ENOSYS)
+ err = do_init_module(mod, flags, args);
+
+ if (err < 0)
+ INFO(mod->ctx, "Failed to insert module '%s': %s\n",
+ path, strerror(-err));
+
+ return err;
+}
+
+static bool module_is_blacklisted(struct kmod_module *mod)
+{
+ struct kmod_ctx *ctx = mod->ctx;
+ const struct kmod_config *config = kmod_get_config(ctx);
+ const struct kmod_list *bl = config->blacklists;
+ const struct kmod_list *l;
+
+ kmod_list_foreach(l, bl) {
+ const char *modname = kmod_blacklist_get_modname(l);
+
+ if (streq(modname, mod->name))
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * kmod_module_apply_filter
+ * @ctx: kmod library context
+ * @filter_type: bitmask to filter modules out, valid types are
+ * KMOD_FILTER_BLACKLIST: filter modules in blacklist out;
+ * KMOD_FILTER_BUILTIN: filter builtin modules out.
+ * @input: list of kmod_module to be filtered
+ * @output: where to save the new list
+ *
+ * Given a list @input, this function filter it out by the filter mask
+ * and save it in @output.
+ *
+ * Returns: 0 on success or < 0 otherwise. @output is saved with the updated
+ * list.
+ */
+KMOD_EXPORT int kmod_module_apply_filter(const struct kmod_ctx *ctx,
+ enum kmod_filter filter_type,
+ const struct kmod_list *input,
+ struct kmod_list **output)
+{
+ const struct kmod_list *li;
+
+ if (ctx == NULL || output == NULL)
+ return -ENOENT;
+
+ *output = NULL;
+ if (input == NULL)
+ return 0;
+
+ kmod_list_foreach(li, input) {
+ struct kmod_module *mod = li->data;
+ struct kmod_list *node;
+
+ if ((filter_type & KMOD_FILTER_BLACKLIST) &&
+ module_is_blacklisted(mod))
+ continue;
+
+ if ((filter_type & KMOD_FILTER_BUILTIN)
+ && kmod_module_is_builtin(mod))
+ continue;
+
+ node = kmod_list_append(*output, mod);
+ if (node == NULL)
+ goto fail;
+
+ *output = node;
+ kmod_module_ref(mod);
+ }
+
+ return 0;
+
+fail:
+ kmod_module_unref_list(*output);
+ *output = NULL;
+ return -ENOMEM;
+}
+
+static int command_do(struct kmod_module *mod, const char *type,
+ const char *cmd)
+{
+ const char *modname = kmod_module_get_name(mod);
+ int err;
+
+ DBG(mod->ctx, "%s %s\n", type, cmd);
+
+ setenv("MODPROBE_MODULE", modname, 1);
+ err = system(cmd);
+ unsetenv("MODPROBE_MODULE");
+
+ if (err == -1) {
+ ERR(mod->ctx, "Could not run %s command '%s' for module %s: %m\n",
+ type, cmd, modname);
+ return -EINVAL;
+ }
+
+ if (WEXITSTATUS(err)) {
+ ERR(mod->ctx, "Error running %s command '%s' for module %s: retcode %d\n",
+ type, cmd, modname, WEXITSTATUS(err));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct probe_insert_cb {
+ int (*run_install)(struct kmod_module *m, const char *cmd, void *data);
+ void *data;
+};
+
+static int module_do_install_commands(struct kmod_module *mod,
+ const char *options,
+ struct probe_insert_cb *cb)
+{
+ const char *command = kmod_module_get_install_commands(mod);
+ char *p;
+ _cleanup_free_ char *cmd;
+ int err;
+ size_t cmdlen, options_len, varlen;
+
+ assert(command);
+
+ if (options == NULL)
+ options = "";
+
+ options_len = strlen(options);
+ cmdlen = strlen(command);
+ varlen = sizeof("$CMDLINE_OPTS") - 1;
+
+ cmd = memdup(command, cmdlen + 1);
+ if (cmd == NULL)
+ return -ENOMEM;
+
+ while ((p = strstr(cmd, "$CMDLINE_OPTS")) != NULL) {
+ size_t prefixlen = p - cmd;
+ size_t suffixlen = cmdlen - prefixlen - varlen;
+ size_t slen = cmdlen - varlen + options_len;
+ char *suffix = p + varlen;
+ char *s = malloc(slen + 1);
+ if (!s)
+ return -ENOMEM;
+
+ memcpy(s, cmd, p - cmd);
+ memcpy(s + prefixlen, options, options_len);
+ memcpy(s + prefixlen + options_len, suffix, suffixlen);
+ s[slen] = '\0';
+
+ free(cmd);
+ cmd = s;
+ cmdlen = slen;
+ }
+
+ if (cb->run_install != NULL)
+ err = cb->run_install(mod, cmd, cb->data);
+ else
+ err = command_do(mod, "install", cmd);
+
+ return err;
+}
+
+static char *module_options_concat(const char *opt, const char *xopt)
+{
+ // TODO: we might need to check if xopt overrides options on opt
+ size_t optlen = opt == NULL ? 0 : strlen(opt);
+ size_t xoptlen = xopt == NULL ? 0 : strlen(xopt);
+ char *r;
+
+ if (optlen == 0 && xoptlen == 0)
+ return NULL;
+
+ r = malloc(optlen + xoptlen + 2);
+
+ if (opt != NULL) {
+ memcpy(r, opt, optlen);
+ r[optlen] = ' ';
+ optlen++;
+ }
+
+ if (xopt != NULL)
+ memcpy(r + optlen, xopt, xoptlen);
+
+ r[optlen + xoptlen] = '\0';
+
+ return r;
+}
+
+static int __kmod_module_get_probe_list(struct kmod_module *mod,
+ bool required,
+ bool ignorecmd,
+ struct kmod_list **list);
+
+/* re-entrant */
+static int __kmod_module_fill_softdep(struct kmod_module *mod,
+ struct kmod_list **list)
+{
+ struct kmod_list *pre = NULL, *post = NULL, *l;
+ int err;
+
+ err = kmod_module_get_softdeps(mod, &pre, &post);
+ if (err < 0) {
+ ERR(mod->ctx, "could not get softdep: %s\n",
+ strerror(-err));
+ goto fail;
+ }
+
+ kmod_list_foreach(l, pre) {
+ struct kmod_module *m = l->data;
+ err = __kmod_module_get_probe_list(m, false, false, list);
+ if (err < 0)
+ goto fail;
+ }
+
+ l = kmod_list_append(*list, kmod_module_ref(mod));
+ if (l == NULL) {
+ kmod_module_unref(mod);
+ err = -ENOMEM;
+ goto fail;
+ }
+ *list = l;
+ mod->ignorecmd = (pre != NULL || post != NULL);
+
+ kmod_list_foreach(l, post) {
+ struct kmod_module *m = l->data;
+ err = __kmod_module_get_probe_list(m, false, false, list);
+ if (err < 0)
+ goto fail;
+ }
+
+fail:
+ kmod_module_unref_list(pre);
+ kmod_module_unref_list(post);
+
+ return err;
+}
+
+/* re-entrant */
+static int __kmod_module_get_probe_list(struct kmod_module *mod,
+ bool required,
+ bool ignorecmd,
+ struct kmod_list **list)
+{
+ struct kmod_list *dep, *l;
+ int err = 0;
+
+ if (mod->visited) {
+ DBG(mod->ctx, "Ignore module '%s': already visited\n",
+ mod->name);
+ return 0;
+ }
+ mod->visited = true;
+
+ dep = kmod_module_get_dependencies(mod);
+ if (required) {
+ /*
+ * Called from kmod_module_probe_insert_module(); set the
+ * ->required flag on mod and all its dependencies before
+ * they are possibly visited through some softdeps.
+ */
+ mod->required = true;
+ kmod_list_foreach(l, dep) {
+ struct kmod_module *m = l->data;
+ m->required = true;
+ }
+ }
+
+ kmod_list_foreach(l, dep) {
+ struct kmod_module *m = l->data;
+ err = __kmod_module_fill_softdep(m, list);
+ if (err < 0)
+ goto finish;
+ }
+
+ if (ignorecmd) {
+ l = kmod_list_append(*list, kmod_module_ref(mod));
+ if (l == NULL) {
+ kmod_module_unref(mod);
+ err = -ENOMEM;
+ goto finish;
+ }
+ *list = l;
+ mod->ignorecmd = true;
+ } else
+ err = __kmod_module_fill_softdep(mod, list);
+
+finish:
+ kmod_module_unref_list(dep);
+ return err;
+}
+
+static int kmod_module_get_probe_list(struct kmod_module *mod,
+ bool ignorecmd,
+ struct kmod_list **list)
+{
+ int err;
+
+ assert(mod != NULL);
+ assert(list != NULL && *list == NULL);
+
+ /*
+ * Make sure we don't get screwed by previous calls to this function
+ */
+ kmod_set_modules_visited(mod->ctx, false);
+ kmod_set_modules_required(mod->ctx, false);
+
+ err = __kmod_module_get_probe_list(mod, true, ignorecmd, list);
+ if (err < 0) {
+ kmod_module_unref_list(*list);
+ *list = NULL;
+ }
+
+ return err;
+}
+
+/**
+ * kmod_module_probe_insert_module:
+ * @mod: kmod module
+ * @flags: flags are not passed to Linux Kernel, but instead they dictate the
+ * behavior of this function, valid flags are
+ * KMOD_PROBE_FORCE_VERMAGIC: ignore kernel version magic;
+ * KMOD_PROBE_FORCE_MODVERSION: ignore symbol version hashes;
+ * KMOD_PROBE_IGNORE_COMMAND: whether the probe should ignore install
+ * commands and softdeps configured in the system;
+ * KMOD_PROBE_IGNORE_LOADED: do not check whether the module is already
+ * live in kernel or not;
+ * KMOD_PROBE_DRY_RUN: dry run, do not insert module, just call the
+ * associated callback function;
+ * KMOD_PROBE_FAIL_ON_LOADED: if KMOD_PROBE_IGNORE_LOADED is not specified
+ * and the module is already live in kernel, the function will fail if this
+ * flag is specified;
+ * KMOD_PROBE_APPLY_BLACKLIST_ALL: probe will apply KMOD_FILTER_BLACKLIST
+ * filter to this module and its dependencies. If any of the dependencies (or
+ * the module) is blacklisted, the probe will fail, unless the blacklisted
+ * module is already live in kernel;
+ * KMOD_PROBE_APPLY_BLACKLIST: probe will fail if the module is blacklisted;
+ * KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY: probe will fail if the module is an
+ * alias and is blacklisted.
+ * @extra_options: module's options to pass to Linux Kernel. It applies only
+ * to @mod, not to its dependencies.
+ * @run_install: function to run when @mod is backed by an install command.
+ * @data: data to give back to @run_install callback
+ * @print_action: function to call with the action being taken (install or
+ * insmod). It's useful for tools like modprobe when running with verbose
+ * output or in dry-run mode.
+ *
+ * Insert a module in Linux kernel resolving dependencies, soft dependencies,
+ * install commands and applying blacklist.
+ *
+ * If @run_install is NULL, this function will fork and exec by calling
+ * system(3). Don't pass a NULL argument in @run_install if your binary is
+ * setuid/setgid (see warning in system(3)). If you need control over the
+ * execution of an install command, give a callback function instead.
+ *
+ * Returns: 0 on success, > 0 if stopped by a reason given in @flags or < 0 on
+ * failure.
+ */
+KMOD_EXPORT int kmod_module_probe_insert_module(struct kmod_module *mod,
+ unsigned int flags, const char *extra_options,
+ int (*run_install)(struct kmod_module *m,
+ const char *cmd, void *data),
+ const void *data,
+ void (*print_action)(struct kmod_module *m,
+ bool install,
+ const char *options))
+{
+ struct kmod_list *list = NULL, *l;
+ struct probe_insert_cb cb;
+ int err;
+
+ if (mod == NULL)
+ return -ENOENT;
+
+ if (!(flags & KMOD_PROBE_IGNORE_LOADED)
+ && module_is_inkernel(mod)) {
+ if (flags & KMOD_PROBE_FAIL_ON_LOADED)
+ return -EEXIST;
+ else
+ return 0;
+ }
+
+ /*
+ * Ugly assignement + check. We need to check if we were told to check
+ * blacklist and also return the reason why we failed.
+ * KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY will take effect only if the
+ * module is an alias, so we also need to check it
+ */
+ if ((mod->alias != NULL && ((err = flags & KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY)))
+ || (err = flags & KMOD_PROBE_APPLY_BLACKLIST_ALL)
+ || (err = flags & KMOD_PROBE_APPLY_BLACKLIST)) {
+ if (module_is_blacklisted(mod))
+ return err;
+ }
+
+ err = kmod_module_get_probe_list(mod,
+ !!(flags & KMOD_PROBE_IGNORE_COMMAND), &list);
+ if (err < 0)
+ return err;
+
+ if (flags & KMOD_PROBE_APPLY_BLACKLIST_ALL) {
+ struct kmod_list *filtered = NULL;
+
+ err = kmod_module_apply_filter(mod->ctx,
+ KMOD_FILTER_BLACKLIST, list, &filtered);
+ if (err < 0)
+ return err;
+
+ kmod_module_unref_list(list);
+ if (filtered == NULL)
+ return KMOD_PROBE_APPLY_BLACKLIST_ALL;
+
+ list = filtered;
+ }
+
+ cb.run_install = run_install;
+ cb.data = (void *) data;
+
+ kmod_list_foreach(l, list) {
+ struct kmod_module *m = l->data;
+ const char *moptions = kmod_module_get_options(m);
+ const char *cmd = kmod_module_get_install_commands(m);
+ char *options;
+
+ if (!(flags & KMOD_PROBE_IGNORE_LOADED)
+ && module_is_inkernel(m)) {
+ DBG(mod->ctx, "Ignoring module '%s': already loaded\n",
+ m->name);
+ err = -EEXIST;
+ goto finish_module;
+ }
+
+ options = module_options_concat(moptions,
+ m == mod ? extra_options : NULL);
+
+ if (cmd != NULL && !m->ignorecmd) {
+ if (print_action != NULL)
+ print_action(m, true, options ?: "");
+
+ if (!(flags & KMOD_PROBE_DRY_RUN))
+ err = module_do_install_commands(m, options,
+ &cb);
+ } else {
+ if (print_action != NULL)
+ print_action(m, false, options ?: "");
+
+ if (!(flags & KMOD_PROBE_DRY_RUN))
+ err = kmod_module_insert_module(m, flags,
+ options);
+ }
+
+ free(options);
+
+finish_module:
+ /*
+ * Treat "already loaded" error. If we were told to stop on
+ * already loaded and the module being loaded is not a softdep
+ * or dep, bail out. Otherwise, just ignore and continue.
+ *
+ * We need to check here because of race conditions. We
+ * checked first if module was already loaded but it may have
+ * been loaded between the check and the moment we try to
+ * insert it.
+ */
+ if (err == -EEXIST && m == mod &&
+ (flags & KMOD_PROBE_FAIL_ON_LOADED))
+ break;
+
+ /*
+ * Ignore errors from softdeps
+ */
+ if (err == -EEXIST || !m->required)
+ err = 0;
+
+ else if (err < 0)
+ break;
+ }
+
+ kmod_module_unref_list(list);
+ return err;
+}
+
+/**
+ * kmod_module_get_options:
+ * @mod: kmod module
+ *
+ * Get options of this kmod module. Options come from the configuration file
+ * and are cached in @mod. The first call to this function will search for
+ * this module in configuration and subsequent calls return the cached string.
+ *
+ * Returns: a string with all the options separated by spaces. This string is
+ * owned by @mod, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_get_options(const struct kmod_module *mod)
+{
+ if (mod == NULL)
+ return NULL;
+
+ if (!mod->init.options) {
+ /* lazy init */
+ struct kmod_module *m = (struct kmod_module *)mod;
+ const struct kmod_list *l;
+ const struct kmod_config *config;
+ char *opts = NULL;
+ size_t optslen = 0;
+
+ config = kmod_get_config(mod->ctx);
+
+ kmod_list_foreach(l, config->options) {
+ const char *modname = kmod_option_get_modname(l);
+ const char *str;
+ size_t len;
+ void *tmp;
+
+ DBG(mod->ctx, "modname=%s mod->name=%s mod->alias=%s\n", modname, mod->name, mod->alias);
+ if (!(streq(modname, mod->name) || (mod->alias != NULL &&
+ streq(modname, mod->alias))))
+ continue;
+
+ DBG(mod->ctx, "passed = modname=%s mod->name=%s mod->alias=%s\n", modname, mod->name, mod->alias);
+ str = kmod_option_get_options(l);
+ len = strlen(str);
+ if (len < 1)
+ continue;
+
+ tmp = realloc(opts, optslen + len + 2);
+ if (tmp == NULL) {
+ free(opts);
+ goto failed;
+ }
+
+ opts = tmp;
+
+ if (optslen > 0) {
+ opts[optslen] = ' ';
+ optslen++;
+ }
+
+ memcpy(opts + optslen, str, len);
+ optslen += len;
+ opts[optslen] = '\0';
+ }
+
+ m->init.options = true;
+ m->options = opts;
+ }
+
+ return mod->options;
+
+failed:
+ ERR(mod->ctx, "out of memory\n");
+ return NULL;
+}
+
+/**
+ * kmod_module_get_install_commands:
+ * @mod: kmod module
+ *
+ * Get install commands for this kmod module. Install commands come from the
+ * configuration file and are cached in @mod. The first call to this function
+ * will search for this module in configuration and subsequent calls return
+ * the cached string. The install commands are returned as they were in the
+ * configuration, concatenated by ';'. No other processing is made in this
+ * string.
+ *
+ * Returns: a string with all install commands separated by semicolons. This
+ * string is owned by @mod, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_get_install_commands(const struct kmod_module *mod)
+{
+ if (mod == NULL)
+ return NULL;
+
+ if (!mod->init.install_commands) {
+ /* lazy init */
+ struct kmod_module *m = (struct kmod_module *)mod;
+ const struct kmod_list *l;
+ const struct kmod_config *config;
+
+ config = kmod_get_config(mod->ctx);
+
+ kmod_list_foreach(l, config->install_commands) {
+ const char *modname = kmod_command_get_modname(l);
+
+ if (fnmatch(modname, mod->name, 0) != 0)
+ continue;
+
+ m->install_commands = kmod_command_get_command(l);
+
+ /*
+ * find only the first command, as modprobe from
+ * module-init-tools does
+ */
+ break;
+ }
+
+ m->init.install_commands = true;
+ }
+
+ return mod->install_commands;
+}
+
+void kmod_module_set_install_commands(struct kmod_module *mod, const char *cmd)
+{
+ mod->init.install_commands = true;
+ mod->install_commands = cmd;
+}
+
+static struct kmod_list *lookup_softdep(struct kmod_ctx *ctx, const char * const * array, unsigned int count)
+{
+ struct kmod_list *ret = NULL;
+ unsigned i;
+
+ for (i = 0; i < count; i++) {
+ const char *depname = array[i];
+ struct kmod_list *lst = NULL;
+ int err;
+
+ err = kmod_module_new_from_lookup(ctx, depname, &lst);
+ if (err < 0) {
+ ERR(ctx, "failed to lookup soft dependency '%s', continuing anyway.\n", depname);
+ continue;
+ } else if (lst != NULL)
+ ret = kmod_list_append_list(ret, lst);
+ }
+ return ret;
+}
+
+/**
+ * kmod_module_get_softdeps:
+ * @mod: kmod module
+ * @pre: where to save the list of preceding soft dependencies.
+ * @post: where to save the list of post soft dependencies.
+ *
+ * Get soft dependencies for this kmod module. Soft dependencies come
+ * from configuration file and are not cached in @mod because it may include
+ * dependency cycles that would make we leak kmod_module. Any call
+ * to this function will search for this module in configuration, allocate a
+ * list and return the result.
+ *
+ * Both @pre and @post are newly created list of kmod_module and
+ * should be unreferenced with kmod_module_unref_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_module_get_softdeps(const struct kmod_module *mod,
+ struct kmod_list **pre,
+ struct kmod_list **post)
+{
+ const struct kmod_list *l;
+ const struct kmod_config *config;
+
+ if (mod == NULL || pre == NULL || post == NULL)
+ return -ENOENT;
+
+ assert(*pre == NULL);
+ assert(*post == NULL);
+
+ config = kmod_get_config(mod->ctx);
+
+ kmod_list_foreach(l, config->softdeps) {
+ const char *modname = kmod_softdep_get_name(l);
+ const char * const *array;
+ unsigned count;
+
+ if (fnmatch(modname, mod->name, 0) != 0)
+ continue;
+
+ array = kmod_softdep_get_pre(l, &count);
+ *pre = lookup_softdep(mod->ctx, array, count);
+ array = kmod_softdep_get_post(l, &count);
+ *post = lookup_softdep(mod->ctx, array, count);
+
+ /*
+ * find only the first command, as modprobe from
+ * module-init-tools does
+ */
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * kmod_module_get_remove_commands:
+ * @mod: kmod module
+ *
+ * Get remove commands for this kmod module. Remove commands come from the
+ * configuration file and are cached in @mod. The first call to this function
+ * will search for this module in configuration and subsequent calls return
+ * the cached string. The remove commands are returned as they were in the
+ * configuration, concatenated by ';'. No other processing is made in this
+ * string.
+ *
+ * Returns: a string with all remove commands separated by semicolons. This
+ * string is owned by @mod, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_get_remove_commands(const struct kmod_module *mod)
+{
+ if (mod == NULL)
+ return NULL;
+
+ if (!mod->init.remove_commands) {
+ /* lazy init */
+ struct kmod_module *m = (struct kmod_module *)mod;
+ const struct kmod_list *l;
+ const struct kmod_config *config;
+
+ config = kmod_get_config(mod->ctx);
+
+ kmod_list_foreach(l, config->remove_commands) {
+ const char *modname = kmod_command_get_modname(l);
+
+ if (fnmatch(modname, mod->name, 0) != 0)
+ continue;
+
+ m->remove_commands = kmod_command_get_command(l);
+
+ /*
+ * find only the first command, as modprobe from
+ * module-init-tools does
+ */
+ break;
+ }
+
+ m->init.remove_commands = true;
+ }
+
+ return mod->remove_commands;
+}
+
+void kmod_module_set_remove_commands(struct kmod_module *mod, const char *cmd)
+{
+ mod->init.remove_commands = true;
+ mod->remove_commands = cmd;
+}
+
+/**
+ * SECTION:libkmod-loaded
+ * @short_description: currently loaded modules
+ *
+ * Information about currently loaded modules, as reported by Linux kernel.
+ * These information are not cached by libkmod and are always read from /sys
+ * and /proc/modules.
+ */
+
+/**
+ * kmod_module_new_from_loaded:
+ * @ctx: kmod library context
+ * @list: where to save the list of loaded modules
+ *
+ * Create a new list of kmod modules with all modules currently loaded in
+ * kernel. It uses /proc/modules to get the names of loaded modules and to
+ * create kmod modules by calling kmod_module_new_from_name() in each of them.
+ * They are put in @list in no particular order.
+ *
+ * The initial refcount is 1, and needs to be decremented to release the
+ * resources of the kmod_module. The returned @list must be released by
+ * calling kmod_module_unref_list(). Since libkmod keeps track of all
+ * kmod_modules created, they are all released upon @ctx destruction too. Do
+ * not unref @ctx before all the desired operations with the returned list are
+ * completed.
+ *
+ * Returns: 0 on success or < 0 on error.
+ */
+KMOD_EXPORT int kmod_module_new_from_loaded(struct kmod_ctx *ctx,
+ struct kmod_list **list)
+{
+ struct kmod_list *l = NULL;
+ FILE *fp;
+ char line[4096];
+
+ if (ctx == NULL || list == NULL)
+ return -ENOENT;
+
+ fp = fopen("/proc/modules", "re");
+ if (fp == NULL) {
+ int err = -errno;
+ ERR(ctx, "could not open /proc/modules: %s\n", strerror(errno));
+ return err;
+ }
+
+ while (fgets(line, sizeof(line), fp)) {
+ struct kmod_module *m;
+ struct kmod_list *node;
+ int err;
+ size_t len = strlen(line);
+ char *saveptr, *name = strtok_r(line, " \t", &saveptr);
+
+ err = kmod_module_new_from_name(ctx, name, &m);
+ if (err < 0) {
+ ERR(ctx, "could not get module from name '%s': %s\n",
+ name, strerror(-err));
+ goto eat_line;
+ }
+
+ node = kmod_list_append(l, m);
+ if (node)
+ l = node;
+ else {
+ ERR(ctx, "out of memory\n");
+ kmod_module_unref(m);
+ }
+eat_line:
+ while (line[len - 1] != '\n' && fgets(line, sizeof(line), fp))
+ len = strlen(line);
+ }
+
+ fclose(fp);
+ *list = l;
+
+ return 0;
+}
+
+/**
+ * kmod_module_initstate_str:
+ * @state: the state as returned by kmod_module_get_initstate()
+ *
+ * Translate a initstate to a string.
+ *
+ * Returns: the string associated to the @state. This string is statically
+ * allocated, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_initstate_str(enum kmod_module_initstate state)
+{
+ switch (state) {
+ case KMOD_MODULE_BUILTIN:
+ return "builtin";
+ case KMOD_MODULE_LIVE:
+ return "live";
+ case KMOD_MODULE_COMING:
+ return "coming";
+ case KMOD_MODULE_GOING:
+ return "going";
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * kmod_module_get_initstate:
+ * @mod: kmod module
+ *
+ * Get the initstate of this @mod, as returned by Linux Kernel, by reading
+ * /sys filesystem.
+ *
+ * Returns: < 0 on error or module state if module is found in kernel, valid states are
+ * KMOD_MODULE_BUILTIN: module is builtin;
+ * KMOD_MODULE_LIVE: module is live in kernel;
+ * KMOD_MODULE_COMING: module is being loaded;
+ * KMOD_MODULE_GOING: module is being unloaded.
+ */
+KMOD_EXPORT int kmod_module_get_initstate(const struct kmod_module *mod)
+{
+ char path[PATH_MAX], buf[32];
+ int fd, err, pathlen;
+
+ if (mod == NULL)
+ return -ENOENT;
+
+ /* remove const: this can only change internal state */
+ if (kmod_module_is_builtin((struct kmod_module *)mod))
+ return KMOD_MODULE_BUILTIN;
+
+ pathlen = snprintf(path, sizeof(path),
+ "/sys/module/%s/initstate", mod->name);
+ if (pathlen >= (int)sizeof(path)) {
+ /* Too long path was truncated */
+ return -ENAMETOOLONG;
+ }
+ fd = open(path, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ err = -errno;
+
+ DBG(mod->ctx, "could not open '%s': %s\n",
+ path, strerror(-err));
+
+ if (pathlen > (int)sizeof("/initstate") - 1) {
+ struct stat st;
+ path[pathlen - (sizeof("/initstate") - 1)] = '\0';
+ if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+ return KMOD_MODULE_COMING;
+ }
+
+ DBG(mod->ctx, "could not open '%s': %s\n",
+ path, strerror(-err));
+ return err;
+ }
+
+ err = read_str_safe(fd, buf, sizeof(buf));
+ close(fd);
+ if (err < 0) {
+ ERR(mod->ctx, "could not read from '%s': %s\n",
+ path, strerror(-err));
+ return err;
+ }
+
+ if (streq(buf, "live\n"))
+ return KMOD_MODULE_LIVE;
+ else if (streq(buf, "coming\n"))
+ return KMOD_MODULE_COMING;
+ else if (streq(buf, "going\n"))
+ return KMOD_MODULE_GOING;
+
+ ERR(mod->ctx, "unknown %s: '%s'\n", path, buf);
+ return -EINVAL;
+}
+
+/**
+ * kmod_module_get_size:
+ * @mod: kmod module
+ *
+ * Get the size of this kmod module as returned by Linux kernel. If supported,
+ * the size is read from the coresize attribute in /sys/module. For older
+ * kernels, this falls back on /proc/modules and searches for the specified
+ * module to get its size.
+ *
+ * Returns: the size of this kmod module.
+ */
+KMOD_EXPORT long kmod_module_get_size(const struct kmod_module *mod)
+{
+ FILE *fp;
+ char line[4096];
+ int lineno = 0;
+ long size = -ENOENT;
+ int dfd, cfd;
+
+ if (mod == NULL)
+ return -ENOENT;
+
+ /* try to open the module dir in /sys. If this fails, don't
+ * bother trying to find the size as we know the module isn't
+ * loaded.
+ */
+ snprintf(line, sizeof(line), "/sys/module/%s", mod->name);
+ dfd = open(line, O_RDONLY|O_CLOEXEC);
+ if (dfd < 0)
+ return -errno;
+
+ /* available as of linux 3.3.x */
+ cfd = openat(dfd, "coresize", O_RDONLY|O_CLOEXEC);
+ if (cfd >= 0) {
+ if (read_str_long(cfd, &size, 10) < 0)
+ ERR(mod->ctx, "failed to read coresize from %s\n", line);
+ close(cfd);
+ goto done;
+ }
+
+ /* fall back on parsing /proc/modules */
+ fp = fopen("/proc/modules", "re");
+ if (fp == NULL) {
+ int err = -errno;
+ ERR(mod->ctx,
+ "could not open /proc/modules: %s\n", strerror(errno));
+ close(dfd);
+ return err;
+ }
+
+ while (fgets(line, sizeof(line), fp)) {
+ size_t len = strlen(line);
+ char *saveptr, *endptr, *tok = strtok_r(line, " \t", &saveptr);
+ long value;
+
+ lineno++;
+ if (tok == NULL || !streq(tok, mod->name))
+ goto eat_line;
+
+ tok = strtok_r(NULL, " \t", &saveptr);
+ if (tok == NULL) {
+ ERR(mod->ctx,
+ "invalid line format at /proc/modules:%d\n", lineno);
+ break;
+ }
+
+ value = strtol(tok, &endptr, 10);
+ if (endptr == tok || *endptr != '\0') {
+ ERR(mod->ctx,
+ "invalid line format at /proc/modules:%d\n", lineno);
+ break;
+ }
+
+ size = value;
+ break;
+eat_line:
+ while (line[len - 1] != '\n' && fgets(line, sizeof(line), fp))
+ len = strlen(line);
+ }
+ fclose(fp);
+
+done:
+ close(dfd);
+ return size;
+}
+
+/**
+ * kmod_module_get_refcnt:
+ * @mod: kmod module
+ *
+ * Get the ref count of this @mod, as returned by Linux Kernel, by reading
+ * /sys filesystem.
+ *
+ * Returns: the reference count on success or < 0 on failure.
+ */
+KMOD_EXPORT int kmod_module_get_refcnt(const struct kmod_module *mod)
+{
+ char path[PATH_MAX];
+ long refcnt;
+ int fd, err;
+
+ if (mod == NULL)
+ return -ENOENT;
+
+ snprintf(path, sizeof(path), "/sys/module/%s/refcnt", mod->name);
+ fd = open(path, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ err = -errno;
+ DBG(mod->ctx, "could not open '%s': %s\n",
+ path, strerror(errno));
+ return err;
+ }
+
+ err = read_str_long(fd, &refcnt, 10);
+ close(fd);
+ if (err < 0) {
+ ERR(mod->ctx, "could not read integer from '%s': '%s'\n",
+ path, strerror(-err));
+ return err;
+ }
+
+ return (int)refcnt;
+}
+
+/**
+ * kmod_module_get_holders:
+ * @mod: kmod module
+ *
+ * Get a list of kmod modules that are holding this @mod, as returned by Linux
+ * Kernel. After use, free the @list by calling kmod_module_unref_list().
+ *
+ * Returns: a new list of kmod modules on success or NULL on failure.
+ */
+KMOD_EXPORT struct kmod_list *kmod_module_get_holders(const struct kmod_module *mod)
+{
+ char dname[PATH_MAX];
+ struct kmod_list *list = NULL;
+ struct dirent *dent;
+ DIR *d;
+
+ if (mod == NULL || mod->ctx == NULL)
+ return NULL;
+
+ snprintf(dname, sizeof(dname), "/sys/module/%s/holders", mod->name);
+
+ d = opendir(dname);
+ if (d == NULL) {
+ ERR(mod->ctx, "could not open '%s': %s\n",
+ dname, strerror(errno));
+ return NULL;
+ }
+
+ for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
+ struct kmod_module *holder;
+ struct kmod_list *l;
+ int err;
+
+ if (dent->d_name[0] == '.') {
+ if (dent->d_name[1] == '\0' ||
+ (dent->d_name[1] == '.' && dent->d_name[2] == '\0'))
+ continue;
+ }
+
+ err = kmod_module_new_from_name(mod->ctx, dent->d_name,
+ &holder);
+ if (err < 0) {
+ ERR(mod->ctx, "could not create module for '%s': %s\n",
+ dent->d_name, strerror(-err));
+ goto fail;
+ }
+
+ l = kmod_list_append(list, holder);
+ if (l != NULL) {
+ list = l;
+ } else {
+ ERR(mod->ctx, "out of memory\n");
+ kmod_module_unref(holder);
+ goto fail;
+ }
+ }
+
+ closedir(d);
+ return list;
+
+fail:
+ closedir(d);
+ kmod_module_unref_list(list);
+ return NULL;
+}
+
+struct kmod_module_section {
+ unsigned long address;
+ char name[];
+};
+
+static void kmod_module_section_free(struct kmod_module_section *section)
+{
+ free(section);
+}
+
+/**
+ * kmod_module_get_sections:
+ * @mod: kmod module
+ *
+ * Get a list of kmod sections of this @mod, as returned by Linux Kernel. The
+ * structure contained in this list is internal to libkmod and their fields
+ * can be obtained by calling kmod_module_section_get_name() and
+ * kmod_module_section_get_address().
+ *
+ * After use, free the @list by calling kmod_module_section_free_list().
+ *
+ * Returns: a new list of kmod module sections on success or NULL on failure.
+ */
+KMOD_EXPORT struct kmod_list *kmod_module_get_sections(const struct kmod_module *mod)
+{
+ char dname[PATH_MAX];
+ struct kmod_list *list = NULL;
+ struct dirent *dent;
+ DIR *d;
+ int dfd;
+
+ if (mod == NULL)
+ return NULL;
+
+ snprintf(dname, sizeof(dname), "/sys/module/%s/sections", mod->name);
+
+ d = opendir(dname);
+ if (d == NULL) {
+ ERR(mod->ctx, "could not open '%s': %s\n",
+ dname, strerror(errno));
+ return NULL;
+ }
+
+ dfd = dirfd(d);
+
+ for (dent = readdir(d); dent; dent = readdir(d)) {
+ struct kmod_module_section *section;
+ struct kmod_list *l;
+ unsigned long address;
+ size_t namesz;
+ int fd, err;
+
+ if (dent->d_name[0] == '.') {
+ if (dent->d_name[1] == '\0' ||
+ (dent->d_name[1] == '.' && dent->d_name[2] == '\0'))
+ continue;
+ }
+
+ fd = openat(dfd, dent->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ ERR(mod->ctx, "could not open '%s/%s': %m\n",
+ dname, dent->d_name);
+ goto fail;
+ }
+
+ err = read_str_ulong(fd, &address, 16);
+ close(fd);
+ if (err < 0) {
+ ERR(mod->ctx, "could not read long from '%s/%s': %m\n",
+ dname, dent->d_name);
+ goto fail;
+ }
+
+ namesz = strlen(dent->d_name) + 1;
+ section = malloc(sizeof(*section) + namesz);
+
+ if (section == NULL) {
+ ERR(mod->ctx, "out of memory\n");
+ goto fail;
+ }
+
+ section->address = address;
+ memcpy(section->name, dent->d_name, namesz);
+
+ l = kmod_list_append(list, section);
+ if (l != NULL) {
+ list = l;
+ } else {
+ ERR(mod->ctx, "out of memory\n");
+ free(section);
+ goto fail;
+ }
+ }
+
+ closedir(d);
+ return list;
+
+fail:
+ closedir(d);
+ kmod_module_unref_list(list);
+ return NULL;
+}
+
+/**
+ * kmod_module_section_get_module_name:
+ * @entry: a list entry representing a kmod module section
+ *
+ * Get the name of a kmod module section.
+ *
+ * After use, free the @list by calling kmod_module_section_free_list().
+ *
+ * Returns: the name of this kmod module section on success or NULL on
+ * failure. The string is owned by the section, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_section_get_name(const struct kmod_list *entry)
+{
+ struct kmod_module_section *section;
+
+ if (entry == NULL)
+ return NULL;
+
+ section = entry->data;
+ return section->name;
+}
+
+/**
+ * kmod_module_section_get_address:
+ * @entry: a list entry representing a kmod module section
+ *
+ * Get the address of a kmod module section.
+ *
+ * After use, free the @list by calling kmod_module_section_free_list().
+ *
+ * Returns: the address of this kmod module section on success or ULONG_MAX
+ * on failure.
+ */
+KMOD_EXPORT unsigned long kmod_module_section_get_address(const struct kmod_list *entry)
+{
+ struct kmod_module_section *section;
+
+ if (entry == NULL)
+ return (unsigned long)-1;
+
+ section = entry->data;
+ return section->address;
+}
+
+/**
+ * kmod_module_section_free_list:
+ * @list: kmod module section list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_section_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_section_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
+
+static struct kmod_elf *kmod_module_get_elf(const struct kmod_module *mod)
+{
+ if (mod->file == NULL) {
+ const char *path = kmod_module_get_path(mod);
+
+ if (path == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ ((struct kmod_module *)mod)->file = kmod_file_open(mod->ctx,
+ path);
+ if (mod->file == NULL)
+ return NULL;
+ }
+
+ return kmod_file_get_elf(mod->file);
+}
+
+struct kmod_module_info {
+ char *key;
+ char value[];
+};
+
+static struct kmod_module_info *kmod_module_info_new(const char *key, size_t keylen, const char *value, size_t valuelen)
+{
+ struct kmod_module_info *info;
+
+ info = malloc(sizeof(struct kmod_module_info) + keylen + valuelen + 2);
+ if (info == NULL)
+ return NULL;
+
+ info->key = (char *)info + sizeof(struct kmod_module_info)
+ + valuelen + 1;
+ memcpy(info->key, key, keylen);
+ info->key[keylen] = '\0';
+ memcpy(info->value, value, valuelen);
+ info->value[valuelen] = '\0';
+ return info;
+}
+
+static void kmod_module_info_free(struct kmod_module_info *info)
+{
+ free(info);
+}
+
+static struct kmod_list *kmod_module_info_append(struct kmod_list **list, const char *key, size_t keylen, const char *value, size_t valuelen)
+{
+ struct kmod_module_info *info;
+ struct kmod_list *n;
+
+ info = kmod_module_info_new(key, keylen, value, valuelen);
+ if (info == NULL)
+ return NULL;
+ n = kmod_list_append(*list, info);
+ if (n != NULL)
+ *list = n;
+ else
+ kmod_module_info_free(info);
+ return n;
+}
+
+static char *kmod_module_hex_to_str(const char *hex, size_t len)
+{
+ char *str;
+ int i;
+ int j;
+ const size_t line_limit = 20;
+ size_t str_len;
+
+ str_len = len * 3; /* XX: or XX\0 */
+ str_len += ((str_len + line_limit - 1) / line_limit - 1) * 3; /* \n\t\t */
+
+ str = malloc(str_len);
+ if (str == NULL)
+ return NULL;
+
+ for (i = 0, j = 0; i < (int)len; i++) {
+ j += sprintf(str + j, "%02X", (unsigned char)hex[i]);
+ if (i < (int)len - 1) {
+ str[j++] = ':';
+
+ if ((i + 1) % line_limit == 0)
+ j += sprintf(str + j, "\n\t\t");
+ }
+ }
+ return str;
+}
+
+static struct kmod_list *kmod_module_info_append_hex(struct kmod_list **list,
+ const char *key,
+ size_t keylen,
+ const char *value,
+ size_t valuelen)
+{
+ char *hex;
+ struct kmod_list *n;
+
+ if (valuelen > 0) {
+ /* Display as 01:12:DE:AD:BE:EF:... */
+ hex = kmod_module_hex_to_str(value, valuelen);
+ if (hex == NULL)
+ goto list_error;
+ n = kmod_module_info_append(list, key, keylen, hex, strlen(hex));
+ free(hex);
+ if (n == NULL)
+ goto list_error;
+ } else {
+ n = kmod_module_info_append(list, key, keylen, NULL, 0);
+ if (n == NULL)
+ goto list_error;
+ }
+
+ return n;
+
+list_error:
+ return NULL;
+}
+
+/**
+ * kmod_module_get_info:
+ * @mod: kmod module
+ * @list: where to return list of module information. Use
+ * kmod_module_info_get_key() and
+ * kmod_module_info_get_value(). Release this list with
+ * kmod_module_info_free_list()
+ *
+ * Get a list of entries in ELF section ".modinfo", these contain
+ * alias, license, depends, vermagic and other keys with respective
+ * values. If the module is signed (CONFIG_MODULE_SIG), information
+ * about the module signature is included as well: signer,
+ * sig_key and sig_hashalgo.
+ *
+ * After use, free the @list by calling kmod_module_info_free_list().
+ *
+ * Returns: number of entries in @list on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_list **list)
+{
+ struct kmod_elf *elf;
+ char **strings;
+ int i, count, ret = -ENOMEM;
+ struct kmod_signature_info sig_info = {};
+
+ if (mod == NULL || list == NULL)
+ return -ENOENT;
+
+ assert(*list == NULL);
+
+ /* remove const: this can only change internal state */
+ if (kmod_module_is_builtin((struct kmod_module *)mod)) {
+ count = kmod_builtin_get_modinfo(mod->ctx,
+ kmod_module_get_name(mod),
+ &strings);
+ if (count < 0)
+ return count;
+ } else {
+ elf = kmod_module_get_elf(mod);
+ if (elf == NULL)
+ return -errno;
+
+ count = kmod_elf_get_strings(elf, ".modinfo", &strings);
+ if (count < 0)
+ return count;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct kmod_list *n;
+ const char *key, *value;
+ size_t keylen, valuelen;
+
+ key = strings[i];
+ value = strchr(key, '=');
+ if (value == NULL) {
+ keylen = strlen(key);
+ valuelen = 0;
+ value = key;
+ } else {
+ keylen = value - key;
+ value++;
+ valuelen = strlen(value);
+ }
+
+ n = kmod_module_info_append(list, key, keylen, value, valuelen);
+ if (n == NULL)
+ goto list_error;
+ }
+
+ if (mod->file && kmod_module_signature_info(mod->file, &sig_info)) {
+ struct kmod_list *n;
+
+ n = kmod_module_info_append(list, "sig_id", strlen("sig_id"),
+ sig_info.id_type, strlen(sig_info.id_type));
+ if (n == NULL)
+ goto list_error;
+ count++;
+
+ n = kmod_module_info_append(list, "signer", strlen("signer"),
+ sig_info.signer, sig_info.signer_len);
+ if (n == NULL)
+ goto list_error;
+ count++;
+
+
+ n = kmod_module_info_append_hex(list, "sig_key", strlen("sig_key"),
+ sig_info.key_id,
+ sig_info.key_id_len);
+ if (n == NULL)
+ goto list_error;
+ count++;
+
+ n = kmod_module_info_append(list,
+ "sig_hashalgo", strlen("sig_hashalgo"),
+ sig_info.hash_algo, strlen(sig_info.hash_algo));
+ if (n == NULL)
+ goto list_error;
+ count++;
+
+ /*
+ * Omit sig_info.algo for now, as these
+ * are currently constant.
+ */
+ n = kmod_module_info_append_hex(list, "signature",
+ strlen("signature"),
+ sig_info.sig,
+ sig_info.sig_len);
+
+ if (n == NULL)
+ goto list_error;
+ count++;
+
+ }
+ ret = count;
+
+list_error:
+ /* aux structures freed in normal case also */
+ kmod_module_signature_info_free(&sig_info);
+
+ if (ret < 0) {
+ kmod_module_info_free_list(*list);
+ *list = NULL;
+ }
+ free(strings);
+ return ret;
+}
+
+/**
+ * kmod_module_info_get_key:
+ * @entry: a list entry representing a kmod module info
+ *
+ * Get the key of a kmod module info.
+ *
+ * Returns: the key of this kmod module info on success or NULL on
+ * failure. The string is owned by the info, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_info_get_key(const struct kmod_list *entry)
+{
+ struct kmod_module_info *info;
+
+ if (entry == NULL)
+ return NULL;
+
+ info = entry->data;
+ return info->key;
+}
+
+/**
+ * kmod_module_info_get_value:
+ * @entry: a list entry representing a kmod module info
+ *
+ * Get the value of a kmod module info.
+ *
+ * Returns: the value of this kmod module info on success or NULL on
+ * failure. The string is owned by the info, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_info_get_value(const struct kmod_list *entry)
+{
+ struct kmod_module_info *info;
+
+ if (entry == NULL)
+ return NULL;
+
+ info = entry->data;
+ return info->value;
+}
+
+/**
+ * kmod_module_info_free_list:
+ * @list: kmod module info list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_info_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_info_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
+
+struct kmod_module_version {
+ uint64_t crc;
+ char symbol[];
+};
+
+static struct kmod_module_version *kmod_module_versions_new(uint64_t crc, const char *symbol)
+{
+ struct kmod_module_version *mv;
+ size_t symbollen = strlen(symbol) + 1;
+
+ mv = malloc(sizeof(struct kmod_module_version) + symbollen);
+ if (mv == NULL)
+ return NULL;
+
+ mv->crc = crc;
+ memcpy(mv->symbol, symbol, symbollen);
+ return mv;
+}
+
+static void kmod_module_version_free(struct kmod_module_version *version)
+{
+ free(version);
+}
+
+/**
+ * kmod_module_get_versions:
+ * @mod: kmod module
+ * @list: where to return list of module versions. Use
+ * kmod_module_version_get_symbol() and
+ * kmod_module_version_get_crc(). Release this list with
+ * kmod_module_versions_free_list()
+ *
+ * Get a list of entries in ELF section "__versions".
+ *
+ * After use, free the @list by calling kmod_module_versions_free_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_module_get_versions(const struct kmod_module *mod, struct kmod_list **list)
+{
+ struct kmod_elf *elf;
+ struct kmod_modversion *versions;
+ int i, count, ret = 0;
+
+ if (mod == NULL || list == NULL)
+ return -ENOENT;
+
+ assert(*list == NULL);
+
+ elf = kmod_module_get_elf(mod);
+ if (elf == NULL)
+ return -errno;
+
+ count = kmod_elf_get_modversions(elf, &versions);
+ if (count < 0)
+ return count;
+
+ for (i = 0; i < count; i++) {
+ struct kmod_module_version *mv;
+ struct kmod_list *n;
+
+ mv = kmod_module_versions_new(versions[i].crc, versions[i].symbol);
+ if (mv == NULL) {
+ ret = -errno;
+ kmod_module_versions_free_list(*list);
+ *list = NULL;
+ goto list_error;
+ }
+
+ n = kmod_list_append(*list, mv);
+ if (n != NULL)
+ *list = n;
+ else {
+ kmod_module_version_free(mv);
+ kmod_module_versions_free_list(*list);
+ *list = NULL;
+ ret = -ENOMEM;
+ goto list_error;
+ }
+ }
+ ret = count;
+
+list_error:
+ free(versions);
+ return ret;
+}
+
+/**
+ * kmod_module_version_get_symbol:
+ * @entry: a list entry representing a kmod module versions
+ *
+ * Get the symbol of a kmod module versions.
+ *
+ * Returns: the symbol of this kmod module versions on success or NULL
+ * on failure. The string is owned by the versions, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_version_get_symbol(const struct kmod_list *entry)
+{
+ struct kmod_module_version *version;
+
+ if (entry == NULL || entry->data == NULL)
+ return NULL;
+
+ version = entry->data;
+ return version->symbol;
+}
+
+/**
+ * kmod_module_version_get_crc:
+ * @entry: a list entry representing a kmod module version
+ *
+ * Get the crc of a kmod module version.
+ *
+ * Returns: the crc of this kmod module version if available, otherwise default to 0.
+ */
+KMOD_EXPORT uint64_t kmod_module_version_get_crc(const struct kmod_list *entry)
+{
+ struct kmod_module_version *version;
+
+ if (entry == NULL || entry->data == NULL)
+ return 0;
+
+ version = entry->data;
+ return version->crc;
+}
+
+/**
+ * kmod_module_versions_free_list:
+ * @list: kmod module versions list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_versions_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_version_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
+
+struct kmod_module_symbol {
+ uint64_t crc;
+ char symbol[];
+};
+
+static struct kmod_module_symbol *kmod_module_symbols_new(uint64_t crc, const char *symbol)
+{
+ struct kmod_module_symbol *mv;
+ size_t symbollen = strlen(symbol) + 1;
+
+ mv = malloc(sizeof(struct kmod_module_symbol) + symbollen);
+ if (mv == NULL)
+ return NULL;
+
+ mv->crc = crc;
+ memcpy(mv->symbol, symbol, symbollen);
+ return mv;
+}
+
+static void kmod_module_symbol_free(struct kmod_module_symbol *symbol)
+{
+ free(symbol);
+}
+
+/**
+ * kmod_module_get_symbols:
+ * @mod: kmod module
+ * @list: where to return list of module symbols. Use
+ * kmod_module_symbol_get_symbol() and
+ * kmod_module_symbol_get_crc(). Release this list with
+ * kmod_module_symbols_free_list()
+ *
+ * Get a list of entries in ELF section ".symtab" or "__ksymtab_strings".
+ *
+ * After use, free the @list by calling kmod_module_symbols_free_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_module_get_symbols(const struct kmod_module *mod, struct kmod_list **list)
+{
+ struct kmod_elf *elf;
+ struct kmod_modversion *symbols;
+ int i, count, ret = 0;
+
+ if (mod == NULL || list == NULL)
+ return -ENOENT;
+
+ assert(*list == NULL);
+
+ elf = kmod_module_get_elf(mod);
+ if (elf == NULL)
+ return -errno;
+
+ count = kmod_elf_get_symbols(elf, &symbols);
+ if (count < 0)
+ return count;
+
+ for (i = 0; i < count; i++) {
+ struct kmod_module_symbol *mv;
+ struct kmod_list *n;
+
+ mv = kmod_module_symbols_new(symbols[i].crc, symbols[i].symbol);
+ if (mv == NULL) {
+ ret = -errno;
+ kmod_module_symbols_free_list(*list);
+ *list = NULL;
+ goto list_error;
+ }
+
+ n = kmod_list_append(*list, mv);
+ if (n != NULL)
+ *list = n;
+ else {
+ kmod_module_symbol_free(mv);
+ kmod_module_symbols_free_list(*list);
+ *list = NULL;
+ ret = -ENOMEM;
+ goto list_error;
+ }
+ }
+ ret = count;
+
+list_error:
+ free(symbols);
+ return ret;
+}
+
+/**
+ * kmod_module_symbol_get_symbol:
+ * @entry: a list entry representing a kmod module symbols
+ *
+ * Get the symbol of a kmod module symbols.
+ *
+ * Returns: the symbol of this kmod module symbols on success or NULL
+ * on failure. The string is owned by the symbols, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_symbol_get_symbol(const struct kmod_list *entry)
+{
+ struct kmod_module_symbol *symbol;
+
+ if (entry == NULL || entry->data == NULL)
+ return NULL;
+
+ symbol = entry->data;
+ return symbol->symbol;
+}
+
+/**
+ * kmod_module_symbol_get_crc:
+ * @entry: a list entry representing a kmod module symbol
+ *
+ * Get the crc of a kmod module symbol.
+ *
+ * Returns: the crc of this kmod module symbol if available, otherwise default to 0.
+ */
+KMOD_EXPORT uint64_t kmod_module_symbol_get_crc(const struct kmod_list *entry)
+{
+ struct kmod_module_symbol *symbol;
+
+ if (entry == NULL || entry->data == NULL)
+ return 0;
+
+ symbol = entry->data;
+ return symbol->crc;
+}
+
+/**
+ * kmod_module_symbols_free_list:
+ * @list: kmod module symbols list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_symbols_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_symbol_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
+
+struct kmod_module_dependency_symbol {
+ uint64_t crc;
+ uint8_t bind;
+ char symbol[];
+};
+
+static struct kmod_module_dependency_symbol *kmod_module_dependency_symbols_new(uint64_t crc, uint8_t bind, const char *symbol)
+{
+ struct kmod_module_dependency_symbol *mv;
+ size_t symbollen = strlen(symbol) + 1;
+
+ mv = malloc(sizeof(struct kmod_module_dependency_symbol) + symbollen);
+ if (mv == NULL)
+ return NULL;
+
+ mv->crc = crc;
+ mv->bind = bind;
+ memcpy(mv->symbol, symbol, symbollen);
+ return mv;
+}
+
+static void kmod_module_dependency_symbol_free(struct kmod_module_dependency_symbol *dependency_symbol)
+{
+ free(dependency_symbol);
+}
+
+/**
+ * kmod_module_get_dependency_symbols:
+ * @mod: kmod module
+ * @list: where to return list of module dependency_symbols. Use
+ * kmod_module_dependency_symbol_get_symbol() and
+ * kmod_module_dependency_symbol_get_crc(). Release this list with
+ * kmod_module_dependency_symbols_free_list()
+ *
+ * Get a list of entries in ELF section ".symtab" or "__ksymtab_strings".
+ *
+ * After use, free the @list by calling
+ * kmod_module_dependency_symbols_free_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_module_get_dependency_symbols(const struct kmod_module *mod, struct kmod_list **list)
+{
+ struct kmod_elf *elf;
+ struct kmod_modversion *symbols;
+ int i, count, ret = 0;
+
+ if (mod == NULL || list == NULL)
+ return -ENOENT;
+
+ assert(*list == NULL);
+
+ elf = kmod_module_get_elf(mod);
+ if (elf == NULL)
+ return -errno;
+
+ count = kmod_elf_get_dependency_symbols(elf, &symbols);
+ if (count < 0)
+ return count;
+
+ for (i = 0; i < count; i++) {
+ struct kmod_module_dependency_symbol *mv;
+ struct kmod_list *n;
+
+ mv = kmod_module_dependency_symbols_new(symbols[i].crc,
+ symbols[i].bind,
+ symbols[i].symbol);
+ if (mv == NULL) {
+ ret = -errno;
+ kmod_module_dependency_symbols_free_list(*list);
+ *list = NULL;
+ goto list_error;
+ }
+
+ n = kmod_list_append(*list, mv);
+ if (n != NULL)
+ *list = n;
+ else {
+ kmod_module_dependency_symbol_free(mv);
+ kmod_module_dependency_symbols_free_list(*list);
+ *list = NULL;
+ ret = -ENOMEM;
+ goto list_error;
+ }
+ }
+ ret = count;
+
+list_error:
+ free(symbols);
+ return ret;
+}
+
+/**
+ * kmod_module_dependency_symbol_get_symbol:
+ * @entry: a list entry representing a kmod module dependency_symbols
+ *
+ * Get the dependency symbol of a kmod module
+ *
+ * Returns: the symbol of this kmod module dependency_symbols on success or NULL
+ * on failure. The string is owned by the dependency_symbols, do not free it.
+ */
+KMOD_EXPORT const char *kmod_module_dependency_symbol_get_symbol(const struct kmod_list *entry)
+{
+ struct kmod_module_dependency_symbol *dependency_symbol;
+
+ if (entry == NULL || entry->data == NULL)
+ return NULL;
+
+ dependency_symbol = entry->data;
+ return dependency_symbol->symbol;
+}
+
+/**
+ * kmod_module_dependency_symbol_get_crc:
+ * @entry: a list entry representing a kmod module dependency_symbol
+ *
+ * Get the crc of a kmod module dependency_symbol.
+ *
+ * Returns: the crc of this kmod module dependency_symbol if available, otherwise default to 0.
+ */
+KMOD_EXPORT uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry)
+{
+ struct kmod_module_dependency_symbol *dependency_symbol;
+
+ if (entry == NULL || entry->data == NULL)
+ return 0;
+
+ dependency_symbol = entry->data;
+ return dependency_symbol->crc;
+}
+
+/**
+ * kmod_module_dependency_symbol_get_bind:
+ * @entry: a list entry representing a kmod module dependency_symbol
+ *
+ * Get the bind type of a kmod module dependency_symbol.
+ *
+ * Returns: the bind of this kmod module dependency_symbol on success
+ * or < 0 on failure.
+ */
+KMOD_EXPORT int kmod_module_dependency_symbol_get_bind(const struct kmod_list *entry)
+{
+ struct kmod_module_dependency_symbol *dependency_symbol;
+
+ if (entry == NULL || entry->data == NULL)
+ return 0;
+
+ dependency_symbol = entry->data;
+ return dependency_symbol->bind;
+}
+
+/**
+ * kmod_module_dependency_symbols_free_list:
+ * @list: kmod module dependency_symbols list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_dependency_symbols_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_dependency_symbol_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
diff --git a/libkmod/libkmod-signature.c b/libkmod/libkmod-signature.c
new file mode 100644
index 0000000..80f6447
--- /dev/null
+++ b/libkmod/libkmod-signature.c
@@ -0,0 +1,358 @@
+/*
+ * libkmod - module signature display
+ *
+ * Copyright (C) 2013 Michal Marek, SUSE
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <endian.h>
+#include <inttypes.h>
+#ifdef ENABLE_OPENSSL
+#include <openssl/pkcs7.h>
+#include <openssl/ssl.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/missing.h>
+#include <shared/util.h>
+
+#include "libkmod-internal.h"
+
+/* These types and tables were copied from the 3.7 kernel sources.
+ * As this is just description of the signature format, it should not be
+ * considered derived work (so libkmod can use the LGPL license).
+ */
+enum pkey_algo {
+ PKEY_ALGO_DSA,
+ PKEY_ALGO_RSA,
+ PKEY_ALGO__LAST
+};
+
+static const char *const pkey_algo[PKEY_ALGO__LAST] = {
+ [PKEY_ALGO_DSA] = "DSA",
+ [PKEY_ALGO_RSA] = "RSA",
+};
+
+enum pkey_hash_algo {
+ PKEY_HASH_MD4,
+ PKEY_HASH_MD5,
+ PKEY_HASH_SHA1,
+ PKEY_HASH_RIPE_MD_160,
+ PKEY_HASH_SHA256,
+ PKEY_HASH_SHA384,
+ PKEY_HASH_SHA512,
+ PKEY_HASH_SHA224,
+ PKEY_HASH_SM3,
+ PKEY_HASH__LAST
+};
+
+const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
+ [PKEY_HASH_MD4] = "md4",
+ [PKEY_HASH_MD5] = "md5",
+ [PKEY_HASH_SHA1] = "sha1",
+ [PKEY_HASH_RIPE_MD_160] = "rmd160",
+ [PKEY_HASH_SHA256] = "sha256",
+ [PKEY_HASH_SHA384] = "sha384",
+ [PKEY_HASH_SHA512] = "sha512",
+ [PKEY_HASH_SHA224] = "sha224",
+ [PKEY_HASH_SM3] = "sm3",
+};
+
+enum pkey_id_type {
+ PKEY_ID_PGP, /* OpenPGP generated key ID */
+ PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
+ PKEY_ID_PKCS7, /* Signature in PKCS#7 message */
+ PKEY_ID_TYPE__LAST
+};
+
+const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = {
+ [PKEY_ID_PGP] = "PGP",
+ [PKEY_ID_X509] = "X509",
+ [PKEY_ID_PKCS7] = "PKCS#7",
+};
+
+/*
+ * Module signature information block.
+ */
+struct module_signature {
+ uint8_t algo; /* Public-key crypto algorithm [enum pkey_algo] */
+ uint8_t hash; /* Digest algorithm [enum pkey_hash_algo] */
+ uint8_t id_type; /* Key identifier type [enum pkey_id_type] */
+ uint8_t signer_len; /* Length of signer's name */
+ uint8_t key_id_len; /* Length of key identifier */
+ uint8_t __pad[3];
+ uint32_t sig_len; /* Length of signature data (big endian) */
+};
+
+static bool fill_default(const char *mem, off_t size,
+ const struct module_signature *modsig, size_t sig_len,
+ struct kmod_signature_info *sig_info)
+{
+ size -= sig_len;
+ sig_info->sig = mem + size;
+ sig_info->sig_len = sig_len;
+
+ size -= modsig->key_id_len;
+ sig_info->key_id = mem + size;
+ sig_info->key_id_len = modsig->key_id_len;
+
+ size -= modsig->signer_len;
+ sig_info->signer = mem + size;
+ sig_info->signer_len = modsig->signer_len;
+
+ sig_info->algo = pkey_algo[modsig->algo];
+ sig_info->hash_algo = pkey_hash_algo[modsig->hash];
+ sig_info->id_type = pkey_id_type[modsig->id_type];
+
+ return true;
+}
+
+#ifdef ENABLE_OPENSSL
+
+struct pkcs7_private {
+ PKCS7 *pkcs7;
+ unsigned char *key_id;
+ BIGNUM *sno;
+ char *hash_algo;
+};
+
+static void pkcs7_free(void *s)
+{
+ struct kmod_signature_info *si = s;
+ struct pkcs7_private *pvt = si->private;
+
+ PKCS7_free(pvt->pkcs7);
+ BN_free(pvt->sno);
+ free(pvt->key_id);
+ free(pvt->hash_algo);
+ free(pvt);
+ si->private = NULL;
+}
+
+static const char *x509_name_to_str(X509_NAME *name)
+{
+ int i;
+ X509_NAME_ENTRY *e;
+ ASN1_STRING *d;
+ ASN1_OBJECT *o;
+ int nid = -1;
+ const char *str;
+
+ for (i = 0; i < X509_NAME_entry_count(name); i++) {
+ e = X509_NAME_get_entry(name, i);
+ o = X509_NAME_ENTRY_get_object(e);
+ nid = OBJ_obj2nid(o);
+ if (nid == NID_commonName)
+ break;
+ }
+ if (nid == -1)
+ return NULL;
+
+ d = X509_NAME_ENTRY_get_data(e);
+ str = (const char *)ASN1_STRING_get0_data(d);
+
+ return str;
+}
+
+static bool fill_pkcs7(const char *mem, off_t size,
+ const struct module_signature *modsig, size_t sig_len,
+ struct kmod_signature_info *sig_info)
+{
+ const char *pkcs7_raw;
+ PKCS7 *pkcs7;
+ STACK_OF(PKCS7_SIGNER_INFO) *sis;
+ PKCS7_SIGNER_INFO *si;
+ PKCS7_ISSUER_AND_SERIAL *is;
+ X509_NAME *issuer;
+ ASN1_INTEGER *sno;
+ ASN1_OCTET_STRING *sig;
+ BIGNUM *sno_bn;
+ X509_ALGOR *dig_alg;
+ X509_ALGOR *sig_alg;
+ const ASN1_OBJECT *o;
+ BIO *in;
+ int len;
+ unsigned char *key_id_str;
+ struct pkcs7_private *pvt;
+ const char *issuer_str;
+ char *hash_algo;
+ int hash_algo_len;
+
+ size -= sig_len;
+ pkcs7_raw = mem + size;
+
+ in = BIO_new_mem_buf(pkcs7_raw, sig_len);
+
+ pkcs7 = d2i_PKCS7_bio(in, NULL);
+ if (pkcs7 == NULL) {
+ BIO_free(in);
+ return false;
+ }
+
+ BIO_free(in);
+
+ sis = PKCS7_get_signer_info(pkcs7);
+ if (sis == NULL)
+ goto err;
+
+ si = sk_PKCS7_SIGNER_INFO_value(sis, 0);
+ if (si == NULL)
+ goto err;
+
+ is = si->issuer_and_serial;
+ if (is == NULL)
+ goto err;
+ issuer = is->issuer;
+ sno = is->serial;
+
+ sig = si->enc_digest;
+ if (sig == NULL)
+ goto err;
+
+ PKCS7_SIGNER_INFO_get0_algs(si, NULL, &dig_alg, &sig_alg);
+
+ sig_info->sig = (const char *)ASN1_STRING_get0_data(sig);
+ sig_info->sig_len = ASN1_STRING_length(sig);
+
+ sno_bn = ASN1_INTEGER_to_BN(sno, NULL);
+ if (sno_bn == NULL)
+ goto err;
+
+ len = BN_num_bytes(sno_bn);
+ key_id_str = malloc(len);
+ if (key_id_str == NULL)
+ goto err2;
+ BN_bn2bin(sno_bn, key_id_str);
+
+ sig_info->key_id = (const char *)key_id_str;
+ sig_info->key_id_len = len;
+
+ issuer_str = x509_name_to_str(issuer);
+ if (issuer_str != NULL) {
+ sig_info->signer = issuer_str;
+ sig_info->signer_len = strlen(issuer_str);
+ }
+
+ X509_ALGOR_get0(&o, NULL, NULL, dig_alg);
+
+ // Use OBJ_obj2txt to calculate string length
+ hash_algo_len = OBJ_obj2txt(NULL, 0, o, 0);
+ if (hash_algo_len < 0)
+ goto err3;
+ hash_algo = malloc(hash_algo_len + 1);
+ if (hash_algo == NULL)
+ goto err3;
+ hash_algo_len = OBJ_obj2txt(hash_algo, hash_algo_len + 1, o, 0);
+ if (hash_algo_len < 0)
+ goto err4;
+
+ // Assign libcrypto hash algo string or number
+ sig_info->hash_algo = hash_algo;
+
+ sig_info->id_type = pkey_id_type[modsig->id_type];
+
+ pvt = malloc(sizeof(*pvt));
+ if (pvt == NULL)
+ goto err4;
+
+ pvt->pkcs7 = pkcs7;
+ pvt->key_id = key_id_str;
+ pvt->sno = sno_bn;
+ pvt->hash_algo = hash_algo;
+ sig_info->private = pvt;
+
+ sig_info->free = pkcs7_free;
+
+ return true;
+err4:
+ free(hash_algo);
+err3:
+ free(key_id_str);
+err2:
+ BN_free(sno_bn);
+err:
+ PKCS7_free(pkcs7);
+ return false;
+}
+
+#else /* ENABLE OPENSSL */
+
+static bool fill_pkcs7(const char *mem, off_t size,
+ const struct module_signature *modsig, size_t sig_len,
+ struct kmod_signature_info *sig_info)
+{
+ sig_info->hash_algo = "unknown";
+ sig_info->id_type = pkey_id_type[modsig->id_type];
+ return true;
+}
+
+#endif /* ENABLE OPENSSL */
+
+#define SIG_MAGIC "~Module signature appended~\n"
+
+/*
+ * A signed module has the following layout:
+ *
+ * [ module ]
+ * [ signer's name ]
+ * [ key identifier ]
+ * [ signature data ]
+ * [ struct module_signature ]
+ * [ SIG_MAGIC ]
+ */
+
+bool kmod_module_signature_info(const struct kmod_file *file, struct kmod_signature_info *sig_info)
+{
+ const char *mem;
+ off_t size;
+ const struct module_signature *modsig;
+ size_t sig_len;
+
+ size = kmod_file_get_size(file);
+ mem = kmod_file_get_contents(file);
+ if (size < (off_t)strlen(SIG_MAGIC))
+ return false;
+ size -= strlen(SIG_MAGIC);
+ if (memcmp(SIG_MAGIC, mem + size, strlen(SIG_MAGIC)) != 0)
+ return false;
+
+ if (size < (off_t)sizeof(struct module_signature))
+ return false;
+ size -= sizeof(struct module_signature);
+ modsig = (struct module_signature *)(mem + size);
+ if (modsig->algo >= PKEY_ALGO__LAST ||
+ modsig->hash >= PKEY_HASH__LAST ||
+ modsig->id_type >= PKEY_ID_TYPE__LAST)
+ return false;
+ sig_len = be32toh(get_unaligned(&modsig->sig_len));
+ if (sig_len == 0 ||
+ size < (int64_t)(modsig->signer_len + modsig->key_id_len + sig_len))
+ return false;
+
+ switch (modsig->id_type) {
+ case PKEY_ID_PKCS7:
+ return fill_pkcs7(mem, size, modsig, sig_len, sig_info);
+ default:
+ return fill_default(mem, size, modsig, sig_len, sig_info);
+ }
+}
+
+void kmod_module_signature_info_free(struct kmod_signature_info *sig_info)
+{
+ if (sig_info->free)
+ sig_info->free(sig_info);
+}
diff --git a/libkmod/libkmod.c b/libkmod/libkmod.c
new file mode 100644
index 0000000..213b424
--- /dev/null
+++ b/libkmod/libkmod.c
@@ -0,0 +1,1024 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include <shared/hash.h>
+#include <shared/util.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+#include "libkmod-index.h"
+
+#define KMOD_HASH_SIZE (256)
+#define KMOD_LRU_MAX (128)
+#define _KMOD_INDEX_MODULES_SIZE KMOD_INDEX_MODULES_BUILTIN + 1
+
+/**
+ * SECTION:libkmod
+ * @short_description: libkmod context
+ *
+ * The context contains the default values for the library user,
+ * and is passed to all library operations.
+ */
+
+static const struct {
+ const char *fn;
+ const char *prefix;
+} index_files[] = {
+ [KMOD_INDEX_MODULES_DEP] = { .fn = "modules.dep", .prefix = "" },
+ [KMOD_INDEX_MODULES_ALIAS] = { .fn = "modules.alias", .prefix = "alias " },
+ [KMOD_INDEX_MODULES_SYMBOL] = { .fn = "modules.symbols", .prefix = "alias "},
+ [KMOD_INDEX_MODULES_BUILTIN_ALIAS] = { .fn = "modules.builtin.alias", .prefix = "" },
+ [KMOD_INDEX_MODULES_BUILTIN] = { .fn = "modules.builtin", .prefix = ""},
+};
+
+static const char *const default_config_paths[] = {
+ SYSCONFDIR "/modprobe.d",
+ "/run/modprobe.d",
+ "/usr/local/lib/modprobe.d",
+ DISTCONFDIR "/modprobe.d",
+ "/lib/modprobe.d",
+ NULL
+};
+
+/**
+ * kmod_ctx:
+ *
+ * Opaque object representing the library context.
+ */
+struct kmod_ctx {
+ int refcount;
+ int log_priority;
+ void (*log_fn)(void *data,
+ int priority, const char *file, int line,
+ const char *fn, const char *format, va_list args);
+ void *log_data;
+ const void *userdata;
+ char *dirname;
+ enum kmod_file_compression_type kernel_compression;
+ struct kmod_config *config;
+ struct hash *modules_by_name;
+ struct index_mm *indexes[_KMOD_INDEX_MODULES_SIZE];
+ unsigned long long indexes_stamp[_KMOD_INDEX_MODULES_SIZE];
+};
+
+void kmod_log(const struct kmod_ctx *ctx,
+ int priority, const char *file, int line, const char *fn,
+ const char *format, ...)
+{
+ va_list args;
+
+ if (ctx->log_fn == NULL)
+ return;
+
+ va_start(args, format);
+ ctx->log_fn(ctx->log_data, priority, file, line, fn, format, args);
+ va_end(args);
+}
+
+_printf_format_(6, 0)
+static void log_filep(void *data,
+ int priority, const char *file, int line,
+ const char *fn, const char *format, va_list args)
+{
+ FILE *fp = data;
+#ifdef ENABLE_DEBUG
+ char buf[16];
+ const char *priname;
+ switch (priority) {
+ case LOG_EMERG:
+ priname = "EMERGENCY";
+ break;
+ case LOG_ALERT:
+ priname = "ALERT";
+ break;
+ case LOG_CRIT:
+ priname = "CRITICAL";
+ break;
+ case LOG_ERR:
+ priname = "ERROR";
+ break;
+ case LOG_WARNING:
+ priname = "WARNING";
+ break;
+ case LOG_NOTICE:
+ priname = "NOTICE";
+ break;
+ case LOG_INFO:
+ priname = "INFO";
+ break;
+ case LOG_DEBUG:
+ priname = "DEBUG";
+ break;
+ default:
+ snprintf(buf, sizeof(buf), "L:%d", priority);
+ priname = buf;
+ }
+ fprintf(fp, "libkmod: %s %s:%d %s: ", priname, file, line, fn);
+#else
+ fprintf(fp, "libkmod: %s: ", fn);
+#endif
+ vfprintf(fp, format, args);
+}
+
+
+/**
+ * kmod_get_dirname:
+ * @ctx: kmod library context
+ *
+ * Retrieve the absolute path used for linux modules in this context. The path
+ * is computed from the arguments to kmod_new().
+ */
+KMOD_EXPORT const char *kmod_get_dirname(const struct kmod_ctx *ctx)
+{
+ return ctx->dirname;
+}
+
+/**
+ * kmod_get_userdata:
+ * @ctx: kmod library context
+ *
+ * Retrieve stored data pointer from library context. This might be useful
+ * to access from callbacks.
+ *
+ * Returns: stored userdata
+ */
+KMOD_EXPORT void *kmod_get_userdata(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;
+ return (void *)ctx->userdata;
+}
+
+/**
+ * kmod_set_userdata:
+ * @ctx: kmod library context
+ * @userdata: data pointer
+ *
+ * Store custom @userdata in the library context.
+ */
+KMOD_EXPORT void kmod_set_userdata(struct kmod_ctx *ctx, const void *userdata)
+{
+ if (ctx == NULL)
+ return;
+ ctx->userdata = userdata;
+}
+
+static int log_priority(const char *priority)
+{
+ char *endptr;
+ int prio;
+
+ prio = strtol(priority, &endptr, 10);
+ if (endptr[0] == '\0' || isspace(endptr[0]))
+ return prio;
+ if (strncmp(priority, "err", 3) == 0)
+ return LOG_ERR;
+ if (strncmp(priority, "info", 4) == 0)
+ return LOG_INFO;
+ if (strncmp(priority, "debug", 5) == 0)
+ return LOG_DEBUG;
+ return 0;
+}
+
+static const char *dirname_default_prefix = MODULE_DIRECTORY;
+
+static char *get_kernel_release(const char *dirname)
+{
+ struct utsname u;
+ char *p;
+
+ if (dirname != NULL)
+ return path_make_absolute_cwd(dirname);
+
+ if (uname(&u) < 0)
+ return NULL;
+
+ if (asprintf(&p, "%s/%s", dirname_default_prefix, u.release) < 0)
+ return NULL;
+
+ return p;
+}
+
+static enum kmod_file_compression_type get_kernel_compression(struct kmod_ctx *ctx)
+{
+ const char *path = "/sys/module/compression";
+ char buf[16];
+ int fd;
+ int err;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ /* Not having the file is not an error: kernel may be too old */
+ DBG(ctx, "could not open '%s' for reading: %m\n", path);
+ return KMOD_FILE_COMPRESSION_NONE;
+ }
+
+ err = read_str_safe(fd, buf, sizeof(buf));
+ close(fd);
+ if (err < 0) {
+ ERR(ctx, "could not read from '%s': %s\n",
+ path, strerror(-err));
+ return KMOD_FILE_COMPRESSION_NONE;
+ }
+
+ if (streq(buf, "zstd\n"))
+ return KMOD_FILE_COMPRESSION_ZSTD;
+ else if (streq(buf, "xz\n"))
+ return KMOD_FILE_COMPRESSION_XZ;
+ else if (streq(buf, "gzip\n"))
+ return KMOD_FILE_COMPRESSION_ZLIB;
+
+ ERR(ctx, "unknown kernel compression %s", buf);
+
+ return KMOD_FILE_COMPRESSION_NONE;
+}
+
+/**
+ * kmod_new:
+ * @dirname: what to consider as linux module's directory, if NULL
+ * defaults to $MODULE_DIRECTORY/`uname -r`. If it's relative,
+ * it's treated as relative to the current working directory.
+ * Otherwise, give an absolute dirname.
+ * @config_paths: ordered array of paths (directories or files) where
+ * to load from user-defined configuration parameters such as
+ * alias, blacklists, commands (install, remove). If NULL
+ * defaults to /etc/modprobe.d, /run/modprobe.d,
+ * /usr/local/lib/modprobe.d, DISTCONFDIR/modprobe.d, and
+ * /lib/modprobe.d. Give an empty vector if configuration should
+ * not be read. This array must be null terminated.
+ *
+ * Create kmod library context. This reads the kmod configuration
+ * and fills in the default values.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the kmod library context.
+ *
+ * Returns: a new kmod library context
+ */
+KMOD_EXPORT struct kmod_ctx *kmod_new(const char *dirname,
+ const char * const *config_paths)
+{
+ const char *env;
+ struct kmod_ctx *ctx;
+ int err;
+
+ ctx = calloc(1, sizeof(struct kmod_ctx));
+ if (!ctx)
+ return NULL;
+
+ ctx->refcount = 1;
+ ctx->log_fn = log_filep;
+ ctx->log_data = stderr;
+ ctx->log_priority = LOG_ERR;
+
+ ctx->dirname = get_kernel_release(dirname);
+
+ /* environment overwrites config */
+ env = secure_getenv("KMOD_LOG");
+ if (env != NULL)
+ kmod_set_log_priority(ctx, log_priority(env));
+
+ ctx->kernel_compression = get_kernel_compression(ctx);
+
+ if (config_paths == NULL)
+ config_paths = default_config_paths;
+ err = kmod_config_new(ctx, &ctx->config, config_paths);
+ if (err < 0) {
+ ERR(ctx, "could not create config\n");
+ goto fail;
+ }
+
+ ctx->modules_by_name = hash_new(KMOD_HASH_SIZE, NULL);
+ if (ctx->modules_by_name == NULL) {
+ ERR(ctx, "could not create by-name hash\n");
+ goto fail;
+ }
+
+ INFO(ctx, "ctx %p created\n", ctx);
+ DBG(ctx, "log_priority=%d\n", ctx->log_priority);
+
+ return ctx;
+
+fail:
+ free(ctx->modules_by_name);
+ free(ctx->dirname);
+ free(ctx);
+ return NULL;
+}
+
+/**
+ * kmod_ref:
+ * @ctx: kmod library context
+ *
+ * Take a reference of the kmod library context.
+ *
+ * Returns: the passed kmod library context
+ */
+KMOD_EXPORT struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;
+ ctx->refcount++;
+ return ctx;
+}
+
+/**
+ * kmod_unref:
+ * @ctx: kmod library context
+ *
+ * Drop a reference of the kmod library context. If the refcount
+ * reaches zero, the resources of the context will be released.
+ *
+ * Returns: the passed kmod library context or NULL if it's freed
+ */
+KMOD_EXPORT struct kmod_ctx *kmod_unref(struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;
+
+ if (--ctx->refcount > 0)
+ return ctx;
+
+ INFO(ctx, "context %p released\n", ctx);
+
+ kmod_unload_resources(ctx);
+ hash_free(ctx->modules_by_name);
+ free(ctx->dirname);
+ if (ctx->config)
+ kmod_config_free(ctx->config);
+
+ free(ctx);
+ return NULL;
+}
+
+/**
+ * kmod_set_log_fn:
+ * @ctx: kmod library context
+ * @log_fn: function to be called for logging messages
+ * @data: data to pass to log function
+ *
+ * The built-in logging writes to stderr. It can be
+ * overridden by a custom function, to plug log messages
+ * into the user's logging functionality.
+ */
+KMOD_EXPORT void kmod_set_log_fn(struct kmod_ctx *ctx,
+ void (*log_fn)(void *data,
+ int priority, const char *file,
+ int line, const char *fn,
+ const char *format, va_list args),
+ const void *data)
+{
+ if (ctx == NULL)
+ return;
+ ctx->log_fn = log_fn;
+ ctx->log_data = (void *)data;
+ INFO(ctx, "custom logging function %p registered\n", log_fn);
+}
+
+/**
+ * kmod_get_log_priority:
+ * @ctx: kmod library context
+ *
+ * Returns: the current logging priority
+ */
+KMOD_EXPORT int kmod_get_log_priority(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return -1;
+ return ctx->log_priority;
+}
+
+/**
+ * kmod_set_log_priority:
+ * @ctx: kmod library context
+ * @priority: the new logging priority
+ *
+ * Set the current logging priority. The value controls which messages
+ * are logged.
+ */
+KMOD_EXPORT void kmod_set_log_priority(struct kmod_ctx *ctx, int priority)
+{
+ if (ctx == NULL)
+ return;
+ ctx->log_priority = priority;
+}
+
+struct kmod_module *kmod_pool_get_module(struct kmod_ctx *ctx,
+ const char *key)
+{
+ struct kmod_module *mod;
+
+ mod = hash_find(ctx->modules_by_name, key);
+
+ DBG(ctx, "get module name='%s' found=%p\n", key, mod);
+
+ return mod;
+}
+
+void kmod_pool_add_module(struct kmod_ctx *ctx, struct kmod_module *mod,
+ const char *key)
+{
+ DBG(ctx, "add %p key='%s'\n", mod, key);
+
+ hash_add(ctx->modules_by_name, key, mod);
+}
+
+void kmod_pool_del_module(struct kmod_ctx *ctx, struct kmod_module *mod,
+ const char *key)
+{
+ DBG(ctx, "del %p key='%s'\n", mod, key);
+
+ hash_del(ctx->modules_by_name, key);
+}
+
+static int kmod_lookup_alias_from_alias_bin(struct kmod_ctx *ctx,
+ enum kmod_index index_number,
+ const char *name,
+ struct kmod_list **list)
+{
+ int err, nmatch = 0;
+ struct index_file *idx;
+ struct index_value *realnames, *realname;
+
+ if (ctx->indexes[index_number] != NULL) {
+ DBG(ctx, "use mmaped index '%s' for name=%s\n",
+ index_files[index_number].fn, name);
+ realnames = index_mm_searchwild(ctx->indexes[index_number],
+ name);
+ } else {
+ char fn[PATH_MAX];
+
+ snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname,
+ index_files[index_number].fn);
+
+ DBG(ctx, "file=%s name=%s\n", fn, name);
+
+ idx = index_file_open(fn);
+ if (idx == NULL)
+ return -ENOSYS;
+
+ realnames = index_searchwild(idx, name);
+ index_file_close(idx);
+ }
+
+ for (realname = realnames; realname; realname = realname->next) {
+ struct kmod_module *mod;
+
+ err = kmod_module_new_from_alias(ctx, name, realname->value, &mod);
+ if (err < 0) {
+ ERR(ctx, "Could not create module for alias=%s realname=%s: %s\n",
+ name, realname->value, strerror(-err));
+ goto fail;
+ }
+
+ *list = kmod_list_append(*list, mod);
+ nmatch++;
+ }
+
+ index_values_free(realnames);
+ return nmatch;
+
+fail:
+ *list = kmod_list_remove_n_latest(*list, nmatch);
+ index_values_free(realnames);
+ return err;
+
+}
+
+int kmod_lookup_alias_from_symbols_file(struct kmod_ctx *ctx, const char *name,
+ struct kmod_list **list)
+{
+ if (!strstartswith(name, "symbol:"))
+ return 0;
+
+ return kmod_lookup_alias_from_alias_bin(ctx, KMOD_INDEX_MODULES_SYMBOL,
+ name, list);
+}
+
+int kmod_lookup_alias_from_aliases_file(struct kmod_ctx *ctx, const char *name,
+ struct kmod_list **list)
+{
+ return kmod_lookup_alias_from_alias_bin(ctx, KMOD_INDEX_MODULES_ALIAS,
+ name, list);
+}
+
+static char *lookup_builtin_file(struct kmod_ctx *ctx, const char *name)
+{
+ char *line;
+
+ if (ctx->indexes[KMOD_INDEX_MODULES_BUILTIN]) {
+ DBG(ctx, "use mmaped index '%s' modname=%s\n",
+ index_files[KMOD_INDEX_MODULES_BUILTIN].fn,
+ name);
+ line = index_mm_search(ctx->indexes[KMOD_INDEX_MODULES_BUILTIN],
+ name);
+ } else {
+ struct index_file *idx;
+ char fn[PATH_MAX];
+
+ snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname,
+ index_files[KMOD_INDEX_MODULES_BUILTIN].fn);
+ DBG(ctx, "file=%s modname=%s\n", fn, name);
+
+ idx = index_file_open(fn);
+ if (idx == NULL) {
+ DBG(ctx, "could not open builtin file '%s'\n", fn);
+ return NULL;
+ }
+
+ line = index_search(idx, name);
+ index_file_close(idx);
+ }
+
+ return line;
+}
+
+int kmod_lookup_alias_from_kernel_builtin_file(struct kmod_ctx *ctx,
+ const char *name,
+ struct kmod_list **list)
+{
+ struct kmod_list *l;
+ int ret;
+
+ assert(*list == NULL);
+
+ ret = kmod_lookup_alias_from_alias_bin(ctx,
+ KMOD_INDEX_MODULES_BUILTIN_ALIAS,
+ name, list);
+
+ kmod_list_foreach(l, *list) {
+ struct kmod_module *mod = l->data;
+ kmod_module_set_builtin(mod, true);
+ }
+
+ return ret;
+}
+
+int kmod_lookup_alias_from_builtin_file(struct kmod_ctx *ctx, const char *name,
+ struct kmod_list **list)
+{
+ char *line;
+ int err = 0;
+
+ assert(*list == NULL);
+
+ line = lookup_builtin_file(ctx, name);
+ if (line != NULL) {
+ struct kmod_module *mod;
+
+ err = kmod_module_new_from_name(ctx, name, &mod);
+ if (err < 0) {
+ ERR(ctx, "Could not create module from name %s: %s\n",
+ name, strerror(-err));
+ goto finish;
+ }
+
+ /* already mark it as builtin since it's being created from
+ * this index */
+ kmod_module_set_builtin(mod, true);
+ *list = kmod_list_append(*list, mod);
+ if (*list == NULL)
+ err = -ENOMEM;
+ }
+
+finish:
+ free(line);
+ return err;
+}
+
+bool kmod_lookup_alias_is_builtin(struct kmod_ctx *ctx, const char *name)
+{
+ _cleanup_free_ char *line;
+
+ line = lookup_builtin_file(ctx, name);
+
+ return line != NULL;
+}
+
+char *kmod_search_moddep(struct kmod_ctx *ctx, const char *name)
+{
+ struct index_file *idx;
+ char fn[PATH_MAX];
+ char *line;
+
+ if (ctx->indexes[KMOD_INDEX_MODULES_DEP]) {
+ DBG(ctx, "use mmaped index '%s' modname=%s\n",
+ index_files[KMOD_INDEX_MODULES_DEP].fn, name);
+ return index_mm_search(ctx->indexes[KMOD_INDEX_MODULES_DEP],
+ name);
+ }
+
+ snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname,
+ index_files[KMOD_INDEX_MODULES_DEP].fn);
+
+ DBG(ctx, "file=%s modname=%s\n", fn, name);
+
+ idx = index_file_open(fn);
+ if (idx == NULL) {
+ DBG(ctx, "could not open moddep file '%s'\n", fn);
+ return NULL;
+ }
+
+ line = index_search(idx, name);
+ index_file_close(idx);
+
+ return line;
+}
+
+int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name,
+ struct kmod_list **list)
+{
+ char *line;
+ int n = 0;
+
+ /*
+ * Module names do not contain ':'. Return early if we know it will
+ * not be found.
+ */
+ if (strchr(name, ':'))
+ return 0;
+
+ line = kmod_search_moddep(ctx, name);
+ if (line != NULL) {
+ struct kmod_module *mod;
+
+ n = kmod_module_new_from_name(ctx, name, &mod);
+ if (n < 0) {
+ ERR(ctx, "Could not create module from name %s: %s\n",
+ name, strerror(-n));
+ goto finish;
+ }
+
+ *list = kmod_list_append(*list, mod);
+ kmod_module_parse_depline(mod, line);
+ }
+
+finish:
+ free(line);
+
+ return n;
+}
+
+int kmod_lookup_alias_from_config(struct kmod_ctx *ctx, const char *name,
+ struct kmod_list **list)
+{
+ struct kmod_config *config = ctx->config;
+ struct kmod_list *l;
+ int err, nmatch = 0;
+
+ kmod_list_foreach(l, config->aliases) {
+ const char *aliasname = kmod_alias_get_name(l);
+ const char *modname = kmod_alias_get_modname(l);
+
+ if (fnmatch(aliasname, name, 0) == 0) {
+ struct kmod_module *mod;
+
+ err = kmod_module_new_from_alias(ctx, aliasname,
+ modname, &mod);
+ if (err < 0) {
+ ERR(ctx, "Could not create module for alias=%s modname=%s: %s\n",
+ name, modname, strerror(-err));
+ goto fail;
+ }
+
+ *list = kmod_list_append(*list, mod);
+ nmatch++;
+ }
+ }
+
+ return nmatch;
+
+fail:
+ *list = kmod_list_remove_n_latest(*list, nmatch);
+ return err;
+}
+
+int kmod_lookup_alias_from_commands(struct kmod_ctx *ctx, const char *name,
+ struct kmod_list **list)
+{
+ struct kmod_config *config = ctx->config;
+ struct kmod_list *l, *node;
+ int err, nmatch = 0;
+
+ kmod_list_foreach(l, config->install_commands) {
+ const char *modname = kmod_command_get_modname(l);
+
+ if (streq(modname, name)) {
+ const char *cmd = kmod_command_get_command(l);
+ struct kmod_module *mod;
+
+ err = kmod_module_new_from_name(ctx, modname, &mod);
+ if (err < 0) {
+ ERR(ctx, "Could not create module from name %s: %s\n",
+ modname, strerror(-err));
+ return err;
+ }
+
+ node = kmod_list_append(*list, mod);
+ if (node == NULL) {
+ ERR(ctx, "out of memory\n");
+ return -ENOMEM;
+ }
+
+ *list = node;
+ nmatch = 1;
+
+ kmod_module_set_install_commands(mod, cmd);
+
+ /*
+ * match only the first one, like modprobe from
+ * module-init-tools does
+ */
+ break;
+ }
+ }
+
+ if (nmatch)
+ return nmatch;
+
+ kmod_list_foreach(l, config->remove_commands) {
+ const char *modname = kmod_command_get_modname(l);
+
+ if (streq(modname, name)) {
+ const char *cmd = kmod_command_get_command(l);
+ struct kmod_module *mod;
+
+ err = kmod_module_new_from_name(ctx, modname, &mod);
+ if (err < 0) {
+ ERR(ctx, "Could not create module from name %s: %s\n",
+ modname, strerror(-err));
+ return err;
+ }
+
+ node = kmod_list_append(*list, mod);
+ if (node == NULL) {
+ ERR(ctx, "out of memory\n");
+ return -ENOMEM;
+ }
+
+ *list = node;
+ nmatch = 1;
+
+ kmod_module_set_remove_commands(mod, cmd);
+
+ /*
+ * match only the first one, like modprobe from
+ * module-init-tools does
+ */
+ break;
+ }
+ }
+
+ return nmatch;
+}
+
+void kmod_set_modules_visited(struct kmod_ctx *ctx, bool visited)
+{
+ struct hash_iter iter;
+ const void *v;
+
+ hash_iter_init(ctx->modules_by_name, &iter);
+ while (hash_iter_next(&iter, NULL, &v))
+ kmod_module_set_visited((struct kmod_module *)v, visited);
+}
+
+void kmod_set_modules_required(struct kmod_ctx *ctx, bool required)
+{
+ struct hash_iter iter;
+ const void *v;
+
+ hash_iter_init(ctx->modules_by_name, &iter);
+ while (hash_iter_next(&iter, NULL, &v))
+ kmod_module_set_required((struct kmod_module *)v, required);
+}
+
+static bool is_cache_invalid(const char *path, unsigned long long stamp)
+{
+ struct stat st;
+
+ if (stat(path, &st) < 0)
+ return true;
+
+ if (stamp != stat_mstamp(&st))
+ return true;
+
+ return false;
+}
+
+/**
+ * kmod_validate_resources:
+ * @ctx: kmod library context
+ *
+ * Check if indexes and configuration files changed on disk and the current
+ * context is not valid anymore.
+ *
+ * Returns: KMOD_RESOURCES_OK if resources are still valid,
+ * KMOD_RESOURCES_MUST_RELOAD if it's sufficient to call
+ * kmod_unload_resources() and kmod_load_resources() or
+ * KMOD_RESOURCES_MUST_RECREATE if @ctx must be re-created.
+ */
+KMOD_EXPORT int kmod_validate_resources(struct kmod_ctx *ctx)
+{
+ struct kmod_list *l;
+ size_t i;
+
+ if (ctx == NULL || ctx->config == NULL)
+ return KMOD_RESOURCES_MUST_RECREATE;
+
+ kmod_list_foreach(l, ctx->config->paths) {
+ struct kmod_config_path *cf = l->data;
+
+ if (is_cache_invalid(cf->path, cf->stamp))
+ return KMOD_RESOURCES_MUST_RECREATE;
+ }
+
+ for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) {
+ char path[PATH_MAX];
+
+ if (ctx->indexes[i] == NULL)
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s.bin", ctx->dirname,
+ index_files[i].fn);
+
+ if (is_cache_invalid(path, ctx->indexes_stamp[i]))
+ return KMOD_RESOURCES_MUST_RELOAD;
+ }
+
+ return KMOD_RESOURCES_OK;
+}
+
+/**
+ * kmod_load_resources:
+ * @ctx: kmod library context
+ *
+ * Load indexes and keep them open in @ctx. This way it's faster to lookup
+ * information within the indexes. If this function is not called before a
+ * search, the necessary index is always opened and closed.
+ *
+ * If user will do more than one or two lookups, insertions, deletions, most
+ * likely it's good to call this function first. Particularly in a daemon like
+ * udev that on bootup issues hundreds of calls to lookup the index, calling
+ * this function will speedup the searches.
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_load_resources(struct kmod_ctx *ctx)
+{
+ int ret = 0;
+ size_t i;
+
+ if (ctx == NULL)
+ return -ENOENT;
+
+ for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) {
+ char path[PATH_MAX];
+
+ if (ctx->indexes[i] != NULL) {
+ INFO(ctx, "Index %s already loaded\n",
+ index_files[i].fn);
+ continue;
+ }
+
+ snprintf(path, sizeof(path), "%s/%s.bin", ctx->dirname,
+ index_files[i].fn);
+ ret = index_mm_open(ctx, path, &ctx->indexes_stamp[i],
+ &ctx->indexes[i]);
+
+ /*
+ * modules.builtin.alias are considered optional since it's
+ * recently added and older installations may not have it;
+ * we allow failing for any reason
+ */
+ if (ret) {
+ if (i != KMOD_INDEX_MODULES_BUILTIN_ALIAS)
+ break;
+ ret = 0;
+ }
+ }
+
+ if (ret)
+ kmod_unload_resources(ctx);
+
+ return ret;
+}
+
+/**
+ * kmod_unload_resources:
+ * @ctx: kmod library context
+ *
+ * Unload all the indexes. This will free the resources to maintain the index
+ * open and all subsequent searches will need to open and close the index.
+ *
+ * User is free to call kmod_load_resources() and kmod_unload_resources() as
+ * many times as wanted during the lifecycle of @ctx. For example, if a daemon
+ * knows that when starting up it will lookup a lot of modules, it could call
+ * kmod_load_resources() and after the first burst of searches is gone, it
+ * could free the resources by calling kmod_unload_resources().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT void kmod_unload_resources(struct kmod_ctx *ctx)
+{
+ size_t i;
+
+ if (ctx == NULL)
+ return;
+
+ for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) {
+ if (ctx->indexes[i] != NULL) {
+ index_mm_close(ctx->indexes[i]);
+ ctx->indexes[i] = NULL;
+ ctx->indexes_stamp[i] = 0;
+ }
+ }
+}
+
+/**
+ * kmod_dump_index:
+ * @ctx: kmod library context
+ * @type: index to dump, valid indexes are
+ * KMOD_INDEX_MODULES_DEP: index of module dependencies;
+ * KMOD_INDEX_MODULES_ALIAS: index of module aliases;
+ * KMOD_INDEX_MODULES_SYMBOL: index of symbol aliases;
+ * KMOD_INDEX_MODULES_BUILTIN: index of builtin module.
+ * @fd: file descriptor to dump index to
+ *
+ * Dump index to file descriptor. Note that this function doesn't use stdio.h
+ * so call fflush() before calling this function to be sure data is written in
+ * order.
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index type,
+ int fd)
+{
+ if (ctx == NULL)
+ return -ENOSYS;
+
+ if (type < 0 || type >= _KMOD_INDEX_MODULES_SIZE)
+ return -ENOENT;
+
+ if (ctx->indexes[type] != NULL) {
+ DBG(ctx, "use mmaped index '%s'\n", index_files[type].fn);
+ index_mm_dump(ctx->indexes[type], fd,
+ index_files[type].prefix);
+ } else {
+ char fn[PATH_MAX];
+ struct index_file *idx;
+
+ snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname,
+ index_files[type].fn);
+
+ DBG(ctx, "file=%s\n", fn);
+
+ idx = index_file_open(fn);
+ if (idx == NULL)
+ return -ENOSYS;
+
+ index_dump(idx, fd, index_files[type].prefix);
+ index_file_close(idx);
+ }
+
+ return 0;
+}
+
+const struct kmod_config *kmod_get_config(const struct kmod_ctx *ctx)
+{
+ return ctx->config;
+}
+
+enum kmod_file_compression_type kmod_get_kernel_compression(const struct kmod_ctx *ctx)
+{
+ return ctx->kernel_compression;
+}
diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h
new file mode 100644
index 0000000..7251aa7
--- /dev/null
+++ b/libkmod/libkmod.h
@@ -0,0 +1,270 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#ifndef _LIBKMOD_H_
+#define _LIBKMOD_H_
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * kmod_ctx
+ *
+ * library user context - reads the config and system
+ * environment, user variables, allows custom logging
+ */
+struct kmod_ctx;
+struct kmod_ctx *kmod_new(const char *dirname, const char * const *config_paths);
+struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx);
+struct kmod_ctx *kmod_unref(struct kmod_ctx *ctx);
+void kmod_set_log_fn(struct kmod_ctx *ctx,
+ void (*log_fn)(void *log_data,
+ int priority, const char *file, int line,
+ const char *fn, const char *format,
+ va_list args),
+ const void *data);
+int kmod_get_log_priority(const struct kmod_ctx *ctx);
+void kmod_set_log_priority(struct kmod_ctx *ctx, int priority);
+void *kmod_get_userdata(const struct kmod_ctx *ctx);
+void kmod_set_userdata(struct kmod_ctx *ctx, const void *userdata);
+
+const char *kmod_get_dirname(const struct kmod_ctx *ctx);
+
+/*
+ * Management of libkmod's resources
+ */
+int kmod_load_resources(struct kmod_ctx *ctx);
+void kmod_unload_resources(struct kmod_ctx *ctx);
+
+enum kmod_resources {
+ KMOD_RESOURCES_OK = 0,
+ KMOD_RESOURCES_MUST_RELOAD = 1,
+ KMOD_RESOURCES_MUST_RECREATE = 2,
+};
+int kmod_validate_resources(struct kmod_ctx *ctx);
+
+enum kmod_index {
+ KMOD_INDEX_MODULES_DEP = 0,
+ KMOD_INDEX_MODULES_ALIAS,
+ KMOD_INDEX_MODULES_SYMBOL,
+ KMOD_INDEX_MODULES_BUILTIN_ALIAS,
+ KMOD_INDEX_MODULES_BUILTIN,
+ /* Padding to make sure enum is not mapped to char */
+ _KMOD_INDEX_PAD = 1U << 31,
+};
+int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index type, int fd);
+
+/*
+ * kmod_list
+ *
+ * access to kmod generated lists
+ */
+struct kmod_list;
+struct kmod_list *kmod_list_next(const struct kmod_list *list,
+ const struct kmod_list *curr);
+struct kmod_list *kmod_list_prev(const struct kmod_list *list,
+ const struct kmod_list *curr);
+struct kmod_list *kmod_list_last(const struct kmod_list *list);
+
+#define kmod_list_foreach(list_entry, first_entry) \
+ for (list_entry = first_entry; \
+ list_entry != NULL; \
+ list_entry = kmod_list_next(first_entry, list_entry))
+
+#define kmod_list_foreach_reverse(list_entry, first_entry) \
+ for (list_entry = kmod_list_last(first_entry); \
+ list_entry != NULL; \
+ list_entry = kmod_list_prev(first_entry, list_entry))
+
+/*
+ * kmod_config_iter
+ *
+ * access to configuration lists - it allows to get each configuration's
+ * key/value stored by kmod
+ */
+struct kmod_config_iter;
+struct kmod_config_iter *kmod_config_get_blacklists(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_install_commands(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_options(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_ctx *ctx);
+const char *kmod_config_iter_get_key(const struct kmod_config_iter *iter);
+const char *kmod_config_iter_get_value(const struct kmod_config_iter *iter);
+bool kmod_config_iter_next(struct kmod_config_iter *iter);
+void kmod_config_iter_free_iter(struct kmod_config_iter *iter);
+
+/*
+ * kmod_module
+ *
+ * Operate on kernel modules
+ */
+struct kmod_module;
+int kmod_module_new_from_name(struct kmod_ctx *ctx, const char *name,
+ struct kmod_module **mod);
+int kmod_module_new_from_path(struct kmod_ctx *ctx, const char *path,
+ struct kmod_module **mod);
+int kmod_module_new_from_lookup(struct kmod_ctx *ctx, const char *given_alias,
+ struct kmod_list **list);
+int kmod_module_new_from_name_lookup(struct kmod_ctx *ctx,
+ const char *modname,
+ struct kmod_module **mod);
+int kmod_module_new_from_loaded(struct kmod_ctx *ctx,
+ struct kmod_list **list);
+
+struct kmod_module *kmod_module_ref(struct kmod_module *mod);
+struct kmod_module *kmod_module_unref(struct kmod_module *mod);
+int kmod_module_unref_list(struct kmod_list *list);
+struct kmod_module *kmod_module_get_module(const struct kmod_list *entry);
+
+
+/* Removal flags */
+enum kmod_remove {
+ KMOD_REMOVE_FORCE = O_TRUNC,
+ KMOD_REMOVE_NOWAIT = O_NONBLOCK, /* always set */
+ /* libkmod-only defines, not passed to kernel */
+ KMOD_REMOVE_NOLOG = 1,
+};
+
+/* Insertion flags */
+enum kmod_insert {
+ KMOD_INSERT_FORCE_VERMAGIC = 0x1,
+ KMOD_INSERT_FORCE_MODVERSION = 0x2,
+};
+
+/* Flags to kmod_module_probe_insert_module() */
+enum kmod_probe {
+ KMOD_PROBE_FORCE_VERMAGIC = 0x00001,
+ KMOD_PROBE_FORCE_MODVERSION = 0x00002,
+ KMOD_PROBE_IGNORE_COMMAND = 0x00004,
+ KMOD_PROBE_IGNORE_LOADED = 0x00008,
+ KMOD_PROBE_DRY_RUN = 0x00010,
+ KMOD_PROBE_FAIL_ON_LOADED = 0x00020,
+
+ /* codes below can be used in return value, too */
+ KMOD_PROBE_APPLY_BLACKLIST_ALL = 0x10000,
+ KMOD_PROBE_APPLY_BLACKLIST = 0x20000,
+ KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY = 0x40000,
+};
+
+/* Flags to kmod_module_apply_filter() */
+enum kmod_filter {
+ KMOD_FILTER_BLACKLIST = 0x00001,
+ KMOD_FILTER_BUILTIN = 0x00002,
+};
+
+int kmod_module_remove_module(struct kmod_module *mod, unsigned int flags);
+int kmod_module_insert_module(struct kmod_module *mod, unsigned int flags,
+ const char *options);
+int kmod_module_probe_insert_module(struct kmod_module *mod,
+ unsigned int flags, const char *extra_options,
+ int (*run_install)(struct kmod_module *m,
+ const char *cmdline, void *data),
+ const void *data,
+ void (*print_action)(struct kmod_module *m, bool install,
+ const char *options));
+
+
+const char *kmod_module_get_name(const struct kmod_module *mod);
+const char *kmod_module_get_path(const struct kmod_module *mod);
+const char *kmod_module_get_options(const struct kmod_module *mod);
+const char *kmod_module_get_install_commands(const struct kmod_module *mod);
+const char *kmod_module_get_remove_commands(const struct kmod_module *mod);
+struct kmod_list *kmod_module_get_dependencies(const struct kmod_module *mod);
+int kmod_module_get_softdeps(const struct kmod_module *mod,
+ struct kmod_list **pre, struct kmod_list **post);
+int kmod_module_get_filtered_blacklist(const struct kmod_ctx *ctx,
+ const struct kmod_list *input,
+ struct kmod_list **output) __attribute__ ((deprecated));
+int kmod_module_apply_filter(const struct kmod_ctx *ctx,
+ enum kmod_filter filter_type,
+ const struct kmod_list *input,
+ struct kmod_list **output);
+
+
+
+/*
+ * Information regarding "live information" from module's state, as returned
+ * by kernel
+ */
+
+enum kmod_module_initstate {
+ KMOD_MODULE_BUILTIN = 0,
+ KMOD_MODULE_LIVE,
+ KMOD_MODULE_COMING,
+ KMOD_MODULE_GOING,
+ /* Padding to make sure enum is not mapped to char */
+ _KMOD_MODULE_PAD = 1U << 31,
+};
+const char *kmod_module_initstate_str(enum kmod_module_initstate state);
+int kmod_module_get_initstate(const struct kmod_module *mod);
+int kmod_module_get_refcnt(const struct kmod_module *mod);
+struct kmod_list *kmod_module_get_holders(const struct kmod_module *mod);
+struct kmod_list *kmod_module_get_sections(const struct kmod_module *mod);
+const char *kmod_module_section_get_name(const struct kmod_list *entry);
+unsigned long kmod_module_section_get_address(const struct kmod_list *entry);
+void kmod_module_section_free_list(struct kmod_list *list);
+long kmod_module_get_size(const struct kmod_module *mod);
+
+
+
+/*
+ * Information retrieved from ELF headers and sections
+ */
+
+int kmod_module_get_info(const struct kmod_module *mod, struct kmod_list **list);
+const char *kmod_module_info_get_key(const struct kmod_list *entry);
+const char *kmod_module_info_get_value(const struct kmod_list *entry);
+void kmod_module_info_free_list(struct kmod_list *list);
+
+int kmod_module_get_versions(const struct kmod_module *mod, struct kmod_list **list);
+const char *kmod_module_version_get_symbol(const struct kmod_list *entry);
+uint64_t kmod_module_version_get_crc(const struct kmod_list *entry);
+void kmod_module_versions_free_list(struct kmod_list *list);
+
+int kmod_module_get_symbols(const struct kmod_module *mod, struct kmod_list **list);
+const char *kmod_module_symbol_get_symbol(const struct kmod_list *entry);
+uint64_t kmod_module_symbol_get_crc(const struct kmod_list *entry);
+void kmod_module_symbols_free_list(struct kmod_list *list);
+
+enum kmod_symbol_bind {
+ KMOD_SYMBOL_NONE = '\0',
+ KMOD_SYMBOL_LOCAL = 'L',
+ KMOD_SYMBOL_GLOBAL = 'G',
+ KMOD_SYMBOL_WEAK = 'W',
+ KMOD_SYMBOL_UNDEF = 'U'
+};
+
+int kmod_module_get_dependency_symbols(const struct kmod_module *mod, struct kmod_list **list);
+const char *kmod_module_dependency_symbol_get_symbol(const struct kmod_list *entry);
+int kmod_module_dependency_symbol_get_bind(const struct kmod_list *entry);
+uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry);
+void kmod_module_dependency_symbols_free_list(struct kmod_list *list);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+#endif
diff --git a/libkmod/libkmod.pc.in b/libkmod/libkmod.pc.in
new file mode 100644
index 0000000..3acca6a
--- /dev/null
+++ b/libkmod/libkmod.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libkmod
+Description: Library to deal with kernel modules
+Version: @VERSION@
+Libs: -L${libdir} -lkmod
+Libs.private: @libzstd_LIBS@ @liblzma_LIBS@ @zlib_LIBS@
+Cflags: -I${includedir}
diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym
new file mode 100644
index 0000000..0c04fda
--- /dev/null
+++ b/libkmod/libkmod.sym
@@ -0,0 +1,94 @@
+LIBKMOD_5 {
+global:
+ kmod_get_log_priority;
+ kmod_get_userdata;
+ kmod_new;
+ kmod_ref;
+ kmod_set_log_fn;
+ kmod_set_log_priority;
+ kmod_set_userdata;
+ kmod_unref;
+ kmod_list_next;
+ kmod_list_prev;
+ kmod_list_last;
+
+ kmod_load_resources;
+ kmod_unload_resources;
+ kmod_validate_resources;
+ kmod_config_get_blacklists;
+ kmod_config_get_install_commands;
+ kmod_config_get_remove_commands;
+ kmod_config_get_aliases;
+ kmod_config_get_options;
+ kmod_config_get_softdeps;
+ kmod_config_iter_get_key;
+ kmod_config_iter_get_value;
+ kmod_config_iter_next;
+ kmod_config_iter_free_iter;
+ kmod_dump_index;
+
+ kmod_module_new_from_name;
+ kmod_module_new_from_path;
+ kmod_module_new_from_lookup;
+ kmod_module_new_from_name_lookup;
+ kmod_module_new_from_loaded;
+ kmod_module_ref;
+ kmod_module_unref;
+ kmod_module_unref_list;
+ kmod_module_get_module;
+ kmod_module_remove_module;
+ kmod_module_insert_module;
+ kmod_module_probe_insert_module;
+
+ kmod_module_get_dependencies;
+ kmod_module_get_softdeps;
+ kmod_module_get_filtered_blacklist;
+
+ kmod_module_get_name;
+ kmod_module_get_path;
+
+ kmod_module_initstate_str;
+ kmod_module_get_initstate;
+ kmod_module_get_refcnt;
+ kmod_module_get_sections;
+ kmod_module_section_free_list;
+ kmod_module_section_get_name;
+ kmod_module_section_get_address;
+ kmod_module_get_holders;
+ kmod_module_get_size;
+
+ kmod_module_get_options;
+ kmod_module_get_install_commands;
+ kmod_module_get_remove_commands;
+
+ kmod_module_get_info;
+ kmod_module_info_get_key;
+ kmod_module_info_get_value;
+ kmod_module_info_free_list;
+ kmod_module_get_versions;
+ kmod_module_version_get_symbol;
+ kmod_module_version_get_crc;
+ kmod_module_versions_free_list;
+ kmod_module_get_symbols;
+ kmod_module_symbol_get_symbol;
+ kmod_module_symbol_get_crc;
+ kmod_module_symbols_free_list;
+
+ kmod_module_get_dependency_symbols;
+ kmod_module_dependency_symbol_get_symbol;
+ kmod_module_dependency_symbol_get_crc;
+ kmod_module_dependency_symbol_get_bind;
+ kmod_module_dependency_symbols_free_list;
+local:
+ *;
+};
+
+LIBKMOD_6 {
+global:
+ kmod_module_apply_filter;
+} LIBKMOD_5;
+
+LIBKMOD_22 {
+global:
+ kmod_get_dirname;
+} LIBKMOD_6;
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644
index 0000000..d59889a
--- /dev/null
+++ b/m4/.gitignore
@@ -0,0 +1,6 @@
+libtool.m4
+ltoptions.m4
+ltsugar.m4
+ltversion.m4
+lt~obsolete.m4
+gtk-doc.m4
diff --git a/m4/attributes.m4 b/m4/attributes.m4
new file mode 100644
index 0000000..bfdb50f
--- /dev/null
+++ b/m4/attributes.m4
@@ -0,0 +1,306 @@
+dnl Macros to check the presence of generic (non-typed) symbols.
+dnl Copyright (c) 2006-2008 Diego Pettenò <flameeyes@gmail.com>
+dnl Copyright (c) 2006-2008 xine project
+dnl Copyright (c) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2, or (at your option)
+dnl any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+dnl 02110-1301, USA.
+dnl
+dnl As a special exception, the copyright owners of the
+dnl macro gives unlimited permission to copy, distribute and modify the
+dnl configure scripts that are the output of Autoconf when processing the
+dnl Macro. You need not follow the terms of the GNU General Public
+dnl License when using or distributing such scripts, even though portions
+dnl of the text of the Macro appear in them. The GNU General Public
+dnl License (GPL) does govern all other use of the material that
+dnl constitutes the Autoconf Macro.
+dnl
+dnl This special exception to the GPL applies to versions of the
+dnl Autoconf Macro released by this project. When you make and
+dnl distribute a modified version of the Autoconf Macro, you may extend
+dnl this special exception to the GPL to apply to your modified version as
+dnl well.
+
+dnl Check if FLAG in ENV-VAR is supported by compiler and append it
+dnl to WHERE-TO-APPEND variable. Note that we invert -Wno-* checks to
+dnl -W* as gcc cannot test for negated warnings.
+dnl CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG])
+
+AC_DEFUN([CC_CHECK_FLAG_APPEND], [
+ AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2],
+ AS_TR_SH([cc_cv_$2_$3]),
+ [eval "AS_TR_SH([cc_save_$2])='${$2}'"
+ eval "AS_TR_SH([$2])='-Werror `echo "$3" | sed 's/^-Wno-/-W/'`'"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; } ])],
+ [eval "AS_TR_SH([cc_cv_$2_$3])='yes'"],
+ [eval "AS_TR_SH([cc_cv_$2_$3])='no'"])
+ eval "AS_TR_SH([$2])='$cc_save_$2'"])
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes],
+ [eval "$1='${$1} $3'"])
+])
+
+dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2])
+AC_DEFUN([CC_CHECK_FLAGS_APPEND], [
+ for flag in $3; do
+ CC_CHECK_FLAG_APPEND($1, $2, $flag)
+ done
+])
+
+dnl Check if the flag is supported by linker (cacheable)
+dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+
+
+dnl Check if the flag is supported by linker
+dnl CC_CHECK_LDFLAGS_SILENT([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+AC_DEFUN([CC_CHECK_LDFLAGS_SILENT], [
+ AC_CACHE_VAL(AS_TR_SH([cc_cv_ldflags_$1]),
+ [ac_save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $1"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 1; }])],
+ [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"],
+ [eval "AS_TR_SH([cc_cv_ldflags_$1])="])
+ LDFLAGS="$ac_save_LDFLAGS"
+ ])
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes],
+ [$2], [$3])
+])
+
+dnl Check if the flag is supported by linker (cacheable)
+dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+
+AC_DEFUN([CC_CHECK_LDFLAGS], [
+ AC_CACHE_CHECK([if $CC supports $1 flag],
+ AS_TR_SH([cc_cv_ldflags_$1]),
+ CC_CHECK_LDFLAGS_SILENT([$1]) dnl Don't execute actions here!
+ )
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes],
+ [$2], [$3])
+])
+
+dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for
+dnl the current linker to avoid undefined references in a shared object.
+AC_DEFUN([CC_NOUNDEFINED], [
+ dnl We check $host for which systems to enable this for.
+ AC_REQUIRE([AC_CANONICAL_HOST])
+
+ case $host in
+ dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads
+ dnl are requested, as different implementations are present; to avoid problems
+ dnl use -Wl,-z,defs only for those platform not behaving this way.
+ *-freebsd* | *-openbsd*) ;;
+ *)
+ dnl First of all check for the --no-undefined variant of GNU ld. This allows
+ dnl for a much more readable command line, so that people can understand what
+ dnl it does without going to look for what the heck -z defs does.
+ for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do
+ CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"])
+ break
+ done
+ ;;
+ esac
+
+ AC_SUBST([LDFLAGS_NOUNDEFINED])
+])
+
+dnl Check for a -Werror flag or equivalent. -Werror is the GCC
+dnl and ICC flag that tells the compiler to treat all the warnings
+dnl as fatal. We usually need this option to make sure that some
+dnl constructs (like attributes) are not simply ignored.
+dnl
+dnl Other compilers don't support -Werror per se, but they support
+dnl an equivalent flag:
+dnl - Sun Studio compiler supports -errwarn=%all
+AC_DEFUN([CC_CHECK_WERROR], [
+ AC_CACHE_CHECK(
+ [for $CC way to treat warnings as errors],
+ [cc_cv_werror],
+ [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror],
+ [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])])
+ ])
+])
+
+AC_DEFUN([CC_CHECK_ATTRIBUTE], [
+ AC_REQUIRE([CC_CHECK_WERROR])
+ AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))],
+ AS_TR_SH([cc_cv_attribute_$1]),
+ [ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $cc_cv_werror"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])],
+ [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"],
+ [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"])
+ CFLAGS="$ac_save_CFLAGS"
+ ])
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes],
+ [AC_DEFINE(
+ AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1,
+ [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))]
+ )
+ $4],
+ [$5])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [
+ CC_CHECK_ATTRIBUTE(
+ [constructor],,
+ [void __attribute__((constructor)) ctor() { int a; }],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_FORMAT], [
+ CC_CHECK_ATTRIBUTE(
+ [format], [format(printf, n, n)],
+ [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [
+ CC_CHECK_ATTRIBUTE(
+ [format_arg], [format_arg(printf)],
+ [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [
+ CC_CHECK_ATTRIBUTE(
+ [visibility_$1], [visibility("$1")],
+ [void __attribute__((visibility("$1"))) $1_function() { }],
+ [$2], [$3])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_NONNULL], [
+ CC_CHECK_ATTRIBUTE(
+ [nonnull], [nonnull()],
+ [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_UNUSED], [
+ CC_CHECK_ATTRIBUTE(
+ [unused], ,
+ [void some_function(void *foo, __attribute__((unused)) void *bar);],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [
+ CC_CHECK_ATTRIBUTE(
+ [sentinel], ,
+ [void some_function(void *foo, ...) __attribute__((sentinel));],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [
+ CC_CHECK_ATTRIBUTE(
+ [deprecated], ,
+ [void some_function(void *foo, ...) __attribute__((deprecated));],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_ALIAS], [
+ CC_CHECK_ATTRIBUTE(
+ [alias], [weak, alias],
+ [void other_function(void *foo) { }
+ void some_function(void *foo) __attribute__((weak, alias("other_function")));],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_MALLOC], [
+ CC_CHECK_ATTRIBUTE(
+ [malloc], ,
+ [void * __attribute__((malloc)) my_alloc(int n);],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_PACKED], [
+ CC_CHECK_ATTRIBUTE(
+ [packed], ,
+ [struct astructure { char a; int b; long c; void *d; } __attribute__((packed));],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_CONST], [
+ CC_CHECK_ATTRIBUTE(
+ [const], ,
+ [int __attribute__((const)) twopow(int n) { return 1 << n; } ],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_FLAG_VISIBILITY], [
+ AC_REQUIRE([CC_CHECK_WERROR])
+ AC_CACHE_CHECK([if $CC supports -fvisibility=hidden],
+ [cc_cv_flag_visibility],
+ [cc_flag_visibility_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $cc_cv_werror"
+ CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden],
+ cc_cv_flag_visibility='yes',
+ cc_cv_flag_visibility='no')
+ CFLAGS="$cc_flag_visibility_save_CFLAGS"])
+
+ AS_IF([test "x$cc_cv_flag_visibility" = "xyes"],
+ [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1,
+ [Define this if the compiler supports the -fvisibility flag])
+ $1],
+ [$2])
+])
+
+AC_DEFUN([CC_CHECK_FUNC_BUILTIN], [
+ m4_pushdef([UPNAME], m4_translit([$1], [-a-z], [_A-Z]))
+ AC_CACHE_CHECK([if compiler has $1 builtin function],
+ [cc_cv_have_$1],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([], [
+ m4_case([$1],
+ [__builtin_clz], [$1(0)],
+ [__builtin_types_compatible_p], [$1(int, int)],
+ [__builtin_uaddl_overflow], [$1(0UL, 0UL, (void*)0)],
+ [__builtin_uaddll_overflow], [$1(0ULL, 0ULL, (void*)0)],
+ [__builtin_expect], [$1(0, 0)]
+ )])],
+ [cc_cv_have_$1=yes],
+ [cc_cv_have_$1=no])])
+
+ AS_IF([test "x$cc_cv_have_$1" = "xyes"],
+ [AC_DEFINE([HAVE_]m4_defn([UPNAME]), 1, [Define to 1 if compiler has $1() builtin function])
+ $2],
+ [AS_IF([test "x$3" = "x"], [AC_MSG_ERROR([*** builtin function not found: $1()])],
+ [AC_DEFINE([HAVE_]m4_defn([UPNAME]), 0)
+ $3])]
+ )
+ m4_popdef([UPNAME])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [
+ AC_REQUIRE([CC_CHECK_WERROR])
+ AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported],
+ [cc_cv_attribute_aligned],
+ [ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $cc_cv_werror"
+ for cc_attribute_align_try in 64 32 16 8 4 2; do
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+ int main() {
+ static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0;
+ return c;
+ }])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
+ done
+ CFLAGS="$ac_save_CFLAGS"
+ ])
+
+ if test "x$cc_cv_attribute_aligned" != "x"; then
+ AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned],
+ [Define the highest alignment supported])
+ fi
+])
diff --git a/m4/features.m4 b/m4/features.m4
new file mode 100644
index 0000000..82a244f
--- /dev/null
+++ b/m4/features.m4
@@ -0,0 +1,48 @@
+# Copyright (c) 2015 Lucas De Marchi <lucas.de.marchi@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, 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 <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+#
+# CC_FEATURE_APPEND([FLAGS], [ENV-TO-CHECK], [FLAG-NAME])
+AC_DEFUN([CC_FEATURE_APPEND], [
+ AS_VAR_PUSHDEF([FLAGS], [$1])dnl
+ AS_VAR_PUSHDEF([ENV_CHECK], [$2])dnl
+ AS_VAR_PUSHDEF([FLAG_NAME], [$3])dnl
+
+ AS_CASE([" AS_VAR_GET(FLAGS) " ],
+ [*" FLAG_NAME "*], [AC_RUN_LOG([: FLAGS already contains FLAG_NAME])],
+ [
+ AS_IF([test "x$FLAGS" != "x"], [AS_VAR_APPEND(FLAGS, " ")])
+ AS_IF([test "x$ENV_CHECK" = "xyes"],
+ [AS_VAR_APPEND(FLAGS, "+FLAG_NAME")],
+ [AS_VAR_APPEND(FLAGS, "-FLAG_NAME")])
+ ]
+ )
+
+ AS_VAR_POPDEF([FLAG_NAME])dnl
+ AS_VAR_POPDEF([ENV_CHECK])dnl
+ AS_VAR_POPDEF([FLAGS])dnl
+])
diff --git a/man/.gitignore b/man/.gitignore
new file mode 100644
index 0000000..a229b2f
--- /dev/null
+++ b/man/.gitignore
@@ -0,0 +1,4 @@
+*.5
+*.8
+Makefile
+Makefile.in
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 0000000..f550091
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,31 @@
+MAN5 = depmod.d.5 modprobe.d.5 modules.dep.5
+MAN8 = kmod.8 depmod.8 insmod.8 lsmod.8 rmmod.8 modprobe.8 modinfo.8
+MAN_STUB = modules.dep.bin.5
+
+AM_V_XSLT = $(AM_V_XSLT_$(V))
+AM_V_XSLT_ = $(AM_V_XSLT_$(AM_DEFAULT_VERBOSITY))
+AM_V_XSLT_0 = @echo " XSLT " $@;
+
+XSLT = $(if $(XSLTPROC), $(XSLTPROC), xsltproc)
+
+if BUILD_TOOLS
+dist_man_MANS = $(MAN5) $(MAN8) $(MAN_STUB)
+modules.dep.bin.5: modules.dep.5
+endif
+
+EXTRA_DIST = $(MAN5:%.5=%.xml) $(MAN8:%.8=%.xml)
+CLEANFILES = $(dist_man_MANS)
+
+%.5 %.8: %.xml
+ $(AM_V_XSLT)if [ '$(distconfdir)' != '/lib' ] ; then \
+ sed -e 's|@DISTCONFDIR@|$(distconfdir)|g' $< ; \
+ else \
+ sed -e '/@DISTCONFDIR@/d' $< ; \
+ fi | \
+ sed -e 's|@MODULE_DIRECTORY@|$(module_directory)|g' | \
+ $(XSLT) \
+ -o $@ \
+ --nonet \
+ --stringparam man.output.quietly 1 \
+ --param funcsynopsis.style "'ansi'" \
+ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl -
diff --git a/man/depmod.d.xml b/man/depmod.d.xml
new file mode 100644
index 0000000..b07e6a2
--- /dev/null
+++ b/man/depmod.d.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="depmod.d">
+ <refentryinfo>
+ <title>depmod.d</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Jon</firstname>
+ <surname>Masters</surname>
+ <email>jcm@jonmasters.org</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Robby</firstname>
+ <surname>Workman</surname>
+ <email>rworkman@slackware.com</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>depmod.d</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>depmod.d</refname>
+ <refpurpose>Configuration directory for depmod</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/lib/depmod.d/*.conf</filename></para>
+ <para><filename>@DISTCONFDIR@/depmod.d/*.conf</filename></para>
+ <para><filename>/usr/local/lib/depmod.d/*.conf</filename></para>
+ <para><filename>/run/depmod.d/*.conf</filename></para>
+ <para><filename>/etc/depmod.d/*.conf</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>The order in which modules are processed by the
+ <command>depmod</command> command can be altered on a global or
+ per-module basis. This is typically useful in cases where built-in
+ kernel modules are complemented by custom built versions of the
+ same and the user wishes to affect the priority of processing in
+ order to override the module version supplied by the kernel.
+ </para>
+ <para>
+ The format of files under <filename>depmod.d</filename> is simple: one
+ command per line, with blank lines and lines starting with '#'
+ ignored (useful for adding comments). A '\' at the end of a line
+ causes it to continue on the next line, which makes the files a
+ bit neater.
+ </para>
+ </refsect1>
+ <refsect1>
+ <title>COMMANDS</title>
+ <variablelist>
+ <varlistentry>
+ <term>search <replaceable>subdirectory...</replaceable>
+ </term>
+ <listitem>
+ <para>
+ This allows you to specify the order in which @MODULE_DIRECTORY@
+ (or other configured module location) subdirectories will
+ be processed by <command>depmod</command>. Directories are
+ listed in order, with the highest priority given to the
+ first listed directory and the lowest priority given to the last
+ directory listed. The special keyword <command>built-in</command>
+ refers to the standard module directories installed by the kernel.
+ Another special keyword <command>external</command> refers to the
+ list of external directories, defined by the
+ <command>external</command> command.
+ </para>
+ <para>
+ By default, depmod will give a higher priority to
+ a directory with the name <command>updates</command>
+ using this built-in search string: "updates built-in"
+ but more complex arrangements are possible and are
+ used in several popular distributions.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>override <replaceable>modulename</replaceable> <replaceable>kernelversion</replaceable> <replaceable>modulesubdirectory</replaceable>
+ </term>
+ <listitem>
+ <para>
+ This command allows you to override which version of a
+ specific module will be used when more than one module
+ sharing the same name is processed by the
+ <command>depmod</command> command. It is possible to
+ specify one kernel or all kernels using the * wildcard.
+ <replaceable>modulesubdirectory</replaceable> is the
+ name of the subdirectory under @MODULE_DIRECTORY@ (or other
+ module location) where the target module is installed.
+ </para>
+ <para>
+ For example, it is possible to override the priority of
+ an updated test module called <command>kmod</command> by
+ specifying the following command: "override kmod * extra".
+ This will ensure that any matching module name installed
+ under the <command>extra</command> subdirectory within
+ @MODULE_DIRECTORY@ (or other module location) will take priority
+ over any likenamed module already provided by the kernel.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>external <replaceable>kernelversion</replaceable>
+ <replaceable>absolutemodulesdirectory...</replaceable>
+ </term>
+ <listitem>
+ <para>
+ This specifies a list of directories, which will be checked
+ according to the priorities in the <command>search</command>
+ command. The order matters also, the first directory has the higher
+ priority.
+ </para>
+ <para>
+ The <replaceable>kernelversion</replaceable> is a POSIX regular
+ expression or * wildcard, like in the <command>override</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>exclude <replaceable>excludedir</replaceable>
+ </term>
+ <listitem>
+ <para>
+ This specifies the trailing directories that will be excluded
+ during the search for kernel modules.
+ </para>
+ <para>
+ The <replaceable>excludedir</replaceable> is the trailing directory
+ to exclude
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1><title>COPYRIGHT</title>
+ <para>
+ This manual page Copyright 2006-2010, Jon Masters, Red Hat, Inc.
+ </para>
+ </refsect1>
+ <refsect1><title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/depmod.xml b/man/depmod.xml
new file mode 100644
index 0000000..fce2a4a
--- /dev/null
+++ b/man/depmod.xml
@@ -0,0 +1,343 @@
+<?xml version='1.0'?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="depmod">
+ <refentryinfo>
+ <title>depmod</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Jon</firstname>
+ <surname>Masters</surname>
+ <email>jcm@jonmasters.org</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Robby</firstname>
+ <surname>Workman</surname>
+ <email>rworkman@slackware.com</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>depmod</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>depmod</refname>
+ <refpurpose>
+ Generate <filename>modules.dep</filename> and map files.
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>depmod</command>
+ <arg><option>-b <replaceable>basedir</replaceable></option></arg>
+ <arg><option>-o <replaceable>outdir</replaceable></option></arg>
+ <arg><option>-e</option></arg>
+ <arg><option>-E <replaceable>Module.symvers</replaceable></option></arg>
+ <arg><option>-F <replaceable>System.map</replaceable></option></arg>
+ <arg><option>-n</option></arg>
+ <arg><option>-v</option></arg>
+ <arg><option>-A</option></arg>
+ <arg><option>-P <replaceable>prefix</replaceable></option></arg>
+ <arg><option>-w</option></arg>
+ <arg><option><replaceable>version</replaceable></option></arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>depmod</command>
+ <arg><option>-e</option></arg>
+ <arg><option>-E <replaceable>Module.symvers</replaceable></option></arg>
+ <arg><option>-F <replaceable>System.map</replaceable></option></arg>
+ <arg><option>-n</option></arg>
+ <arg><option>-v</option></arg>
+ <arg><option>-P <replaceable>prefix</replaceable></option></arg>
+ <arg><option>-w</option></arg>
+ <arg><option><replaceable>version</replaceable></option></arg>
+ <arg rep='repeat'><option><replaceable>filename</replaceable></option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>
+ Linux kernel modules can provide services (called "symbols") for other
+ modules to use (using one of the EXPORT_SYMBOL variants in the code). If
+ a second module uses this symbol, that second module clearly depends on
+ the first module. These dependencies can get quite complex.
+ </para>
+ <para> <command>depmod</command> creates a list of module dependencies by
+ reading each module under
+ <filename>@MODULE_DIRECTORY@/</filename><replaceable>version</replaceable> and
+ determining what symbols it exports and what symbols it needs. By
+ default, this list is written to <filename>modules.dep</filename>, and a
+ binary hashed version named <filename>modules.dep.bin</filename>, in the
+ same directory. If filenames are given on the command line, only those
+ modules are examined (which is rarely useful unless all modules are
+ listed). <command>depmod</command> also creates a list of symbols
+ provided by modules in the file named
+ <filename>modules.symbols</filename> and its binary hashed version,
+ <filename>modules.symbols.bin</filename>. Finally,
+ <command>depmod</command> will output a file named
+ <filename>modules.devname</filename> if modules supply special device
+ names (devname) that should be populated in /dev on boot (by a utility
+ such as systemd-tmpfiles).
+ </para>
+ <para> If a <replaceable>version</replaceable> is provided, then that kernel
+ version's module directory is used rather than the current kernel version
+ (as returned by <command>uname -r</command>).
+ </para>
+ </refsect1>
+ <refsect1><title>OPTIONS</title>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-a</option>
+ </term>
+ <term>
+ <option>--all</option>
+ </term>
+ <listitem>
+ <para>
+ Probe all modules. This option is enabled by default if no
+ file names are given in the command-line.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-A</option>
+ </term>
+ <term>
+ <option>--quick</option>
+ </term>
+ <listitem>
+ <para>
+ This option scans to see if any modules are newer than the
+ <filename>modules.dep</filename> file before any work is done:
+ if not, it silently exits rather than regenerating the files.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-b <replaceable>basedir</replaceable></option>
+ </term>
+ <term>
+ <option>--basedir <replaceable>basedir</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ If your modules are not currently in the (normal) directory
+ <filename>@MODULE_DIRECTORY@/</filename><replaceable>version</replaceable>,
+ but in a staging area, you can specify a
+ <replaceable>basedir</replaceable> which is prepended to the
+ directory name. This <replaceable>basedir</replaceable> is
+ stripped from the resulting <filename>modules.dep</filename> file,
+ so it is ready to be moved into the normal location. Use this
+ option if you are a distribution vendor who needs to pre-generate
+ the meta-data files rather than running depmod again later.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-o <replaceable>outdir</replaceable></option>
+ </term>
+ <term>
+ <option>--outdir <replaceable>outdir</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Set the output directory where depmod will store any generated file.
+ <replaceable>outdir</replaceable> serves as a root to that location,
+ similar to how <replaceable>basedir</replaceable> is used. Also this
+ setting takes precedence and if used together with
+ <replaceable>basedir</replaceable> it will result in the input being
+ that directory, but the output being the one set by
+ <replaceable>outdir</replaceable>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-C</option>
+ </term>
+ <term>
+ <option>--config <replaceable>file or directory</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ This option overrides the default configuration directory at
+ <filename>/etc/depmod.d/</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-e</option>
+ </term>
+ <term>
+ <option>--errsyms</option>
+ </term>
+ <listitem>
+ <para>
+ When combined with the <option>-F</option> option, this reports any
+ symbols which a module needs which are not supplied by other
+ modules or the kernel. Normally, any symbols not provided by
+ modules are assumed to be provided by the kernel (which should be
+ true in a perfect world), but this assumption can break especially
+ when additionally updated third party drivers are not correctly
+ installed or were built incorrectly.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-E</option>
+ </term>
+ <term>
+ <option>--symvers</option>
+ </term>
+ <listitem>
+ <para>
+ When combined with the <option>-e</option> option, this
+ reports any symbol versions supplied by modules that do
+ not match with the symbol versions provided by the
+ kernel in its <filename>Module.symvers</filename>.
+ This option is mutually incompatible with <option>-F</option>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-F</option>
+ </term>
+ <term>
+ <option>--filesyms <replaceable>System.map</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Supplied with the <filename>System.map</filename> produced when the
+ kernel was built, this allows the <option>-e</option> option to
+ report unresolved symbols. This option is mutually incompatible
+ with <option>-E</option>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-h</option>
+ </term>
+ <term>
+ <option>--help</option>
+ </term>
+ <listitem>
+ <para>
+ Print the help message and exit.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-n</option>
+ </term>
+ <term>
+ <option>--show</option>
+ </term>
+ <term>
+ <option>--dry-run</option>
+ </term>
+ <listitem>
+ <para>
+ This sends the resulting modules.dep and the various map files to
+ standard output rather than writing them into the module directory.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-P</option>
+ </term>
+ <listitem>
+ <para>
+ Some architectures prefix symbols with an extraneous character.
+ This specifies a prefix character (for example '_') to ignore.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-v</option>
+ </term>
+ <term>
+ <option>--verbose</option>
+ </term>
+ <listitem>
+ <para>
+ In verbose mode, <command>depmod</command> will print (to stdout)
+ all the symbols each module depends on and the module's file name
+ which provides that symbol.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-V</option>
+ </term>
+ <term>
+ <option>--version</option>
+ </term>
+ <listitem>
+ <para>
+ Show version of program and exit. See below for caveats when
+ run on older kernels.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-w</option>
+ </term>
+ <listitem>
+ <para>
+ Warn on duplicate dependencies, aliases, symbol versions, etc.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1><title>COPYRIGHT</title>
+ <para>
+ This manual page originally Copyright 2002, Rusty Russell,
+ IBM Corporation. Portions Copyright Jon Masters, and others.
+ </para>
+ </refsect1>
+
+ <refsect1><title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>depmod.d</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modules.dep</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/insmod.xml b/man/insmod.xml
new file mode 100644
index 0000000..3ebdccd
--- /dev/null
+++ b/man/insmod.xml
@@ -0,0 +1,87 @@
+<?xml version='1.0'?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="insmod">
+ <refentryinfo>
+ <title>insmod</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Jon</firstname>
+ <surname>Masters</surname>
+ <email>jcm@jonmasters.org</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>insmod</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>insmod</refname>
+ <refpurpose>
+ Simple program to insert a module into the Linux Kernel
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>insmod</command>
+ <arg><replaceable>filename</replaceable></arg>
+ <arg rep='repeat'><replaceable>module options</replaceable></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>
+ <command>insmod</command> is a trivial program to insert a module into
+ the kernel. Most users will want to use
+ <citerefentry><refentrytitle>modprobe</refentrytitle>
+ <manvolnum>8</manvolnum> </citerefentry> instead, which is more clever
+ and can handle module dependencies.
+ </para>
+ <para>
+ Only the most general of error messages are reported: as the work of
+ trying to link the module is now done inside the kernel, the
+ <command>dmesg</command> usually gives more information about errors.
+ </para>
+ </refsect1>
+
+ <refsect1><title>COPYRIGHT</title>
+ <para>
+ This manual page originally Copyright 2002, Rusty Russell, IBM
+ Corporation. Maintained by Jon Masters and others.
+ </para>
+ </refsect1>
+
+ <refsect1><title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>rmmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>lsmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modinfo</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ <citerefentry>
+ <refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/kmod.xml b/man/kmod.xml
new file mode 100644
index 0000000..0706ad5
--- /dev/null
+++ b/man/kmod.xml
@@ -0,0 +1,120 @@
+<?xml version='1.0'?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="kmod">
+ <refentryinfo>
+ <title>kmod</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>kmod</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>kmod</refname>
+ <refpurpose>Program to manage Linux Kernel modules</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>kmod</command>
+ <arg rep='repeat'><option>OPTIONS</option></arg>
+ <arg><replaceable>COMMAND</replaceable></arg>
+ <arg rep='repeat'><option>COMMAND_OPTIONS</option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>
+ <command>kmod</command> is a multi-call binary which implements the
+ programs used to control Linux Kernel modules. Most users will only
+ run it using its other names.
+ </para>
+ </refsect1>
+
+ <refsect1><title>OPTIONS</title>
+ <variablelist>
+ <varlistentry>
+ <term><option>-V</option> <option>--version</option>
+ </term>
+ <listitem>
+ <para>Show the program version and exit.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-h</option> <option>--help</option>
+ </term>
+ <listitem>
+ <para>Show the help message.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1><title>COMMANDS</title>
+ <variablelist>
+ <varlistentry>
+ <term><command>help</command></term>
+ <listitem>
+ <para>Show the help message.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><command>list</command></term>
+ <listitem>
+ <para>List the currently loaded modules.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><command>static-nodes</command></term>
+ <listitem>
+ <para>Output the static device nodes information provided by
+ the modules of the currently running kernel version.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>COPYRIGHT</title>
+ <para>
+ This manual page originally Copyright 2014, Marco d'Itri.
+ Maintained by Lucas De Marchi and others.
+ </para>
+ </refsect1>
+
+ <refsect1><title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>lsmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>rmmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>insmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modinfo</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/lsmod.xml b/man/lsmod.xml
new file mode 100644
index 0000000..588f228
--- /dev/null
+++ b/man/lsmod.xml
@@ -0,0 +1,73 @@
+<?xml version='1.0'?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="lsmod">
+ <refentryinfo>
+ <title>lsmod</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Jon</firstname>
+ <surname>Masters</surname>
+ <email>jcm@jonmasters.org</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>lsmod</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>lsmod</refname>
+ <refpurpose>Show the status of modules in the Linux Kernel</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>lsmod</command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>
+ <command>lsmod</command> is a trivial program which nicely formats the
+ contents of the <filename>/proc/modules</filename>, showing what kernel
+ modules are currently loaded.
+ </para>
+ </refsect1>
+
+ <refsect1><title>COPYRIGHT</title>
+ <para>
+ This manual page originally Copyright 2002, Rusty Russell, IBM
+ Corporation. Maintained by Jon Masters and others.
+ </para>
+ </refsect1>
+
+ <refsect1><title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>insmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modinfo</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ <citerefentry>
+ <refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/modinfo.xml b/man/modinfo.xml
new file mode 100644
index 0000000..b6c4d60
--- /dev/null
+++ b/man/modinfo.xml
@@ -0,0 +1,201 @@
+<?xml version='1.0'?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="modinfo">
+ <refentryinfo>
+ <title>modinfo</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Jon</firstname>
+ <surname>Masters</surname>
+ <email>jcm@jonmasters.org</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>modinfo</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>modinfo</refname>
+ <refpurpose>Show information about a Linux Kernel module</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>modinfo</command>
+ <arg><option>-0</option></arg>
+ <arg><option>-F <replaceable>field</replaceable></option></arg>
+ <arg><option>-k <replaceable>kernel</replaceable></option></arg>
+ <arg rep='repeat'>modulename|filename</arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>modinfo -V</command>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>modinfo -h</command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>
+ <command>modinfo</command> extracts information from the Linux Kernel
+ modules given on the command line. If the module name is not a filename,
+ then the
+ <filename>@MODULE_DIRECTORY@/</filename><replaceable>version</replaceable>
+ directory is searched, as is also done by
+ <citerefentry><refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ when loading kernel modules.
+ </para>
+ <para>
+ <command>modinfo</command> by default lists each attribute of the module
+ in form <replaceable>fieldname</replaceable> :
+ <replaceable>value</replaceable>, for easy reading. The filename is
+ listed the same way (although it's not really an attribute).
+ </para>
+ <para>
+ This version of <command>modinfo</command> can understand modules of any
+ Linux Kernel architecture.
+ </para>
+ </refsect1>
+
+ <refsect1><title>OPTIONS</title>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-V</option>
+ </term>
+ <term>
+ <option>--version</option>
+ </term>
+ <listitem>
+ <para>
+ Print the modinfo version.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-F</option>
+ </term>
+ <term>
+ <option>--field</option>
+ </term>
+ <listitem>
+ <para>
+ Only print this field value, one per line. This is most useful for
+ scripts. Field names are case-insensitive. Common fields (which
+ may not be in every module) include <literal>author</literal>,
+ <literal>description</literal>, <literal>license</literal>,
+ <literal>parm</literal>, <literal>depends</literal>, and
+ <literal>alias</literal>. There are often multiple
+ <literal>parm</literal>, <literal>alias</literal> and
+ <literal>depends</literal> fields. The special field
+ <literal>filename</literal> lists the filename of the module.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-b <replaceable>basedir</replaceable></option>
+ </term>
+ <term>
+ <option>--basedir <replaceable>basedir</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Root directory for modules, <filename>/</filename> by default.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-k <replaceable>kernel</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Provide information about a kernel other than the running one. This
+ is particularly useful for distributions needing to extract
+ information from a newly installed (but not yet running) set of
+ kernel modules. For example, you wish to find which firmware files
+ are needed by various modules in a new kernel for which you must
+ make an initrd/initramfs image prior to booting.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-0</option>
+ </term>
+ <term>
+ <option>--null</option>
+ </term>
+ <listitem>
+ <para>
+ Use the ASCII zero character to separate field values, instead of a
+ new line. This is useful for scripts, since a new line can
+ theoretically appear inside a field.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-a</option>
+ <option>--author</option>
+ </term>
+ <term>
+ <option>-d</option>
+ <option>--description</option>
+ </term>
+ <term>
+ <option>-l</option>
+ <option>--license</option>
+ </term>
+ <term>
+ <option>-p</option>
+ <option>--parameters</option>
+ </term>
+ <term>
+ <option>-n</option>
+ <option>--filename</option>
+ </term>
+ <listitem>
+ <para>
+ These are shortcuts for the <option>--field</option> flag's
+ <literal>author</literal>, <literal>description</literal>,
+ <literal>license</literal>, <literal>parm</literal> and
+ <literal>filename</literal> arguments, to ease the transition
+ from the old modutils <command>modinfo</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1><title>COPYRIGHT</title>
+ <para>
+ This manual page originally Copyright 2003, Rusty Russell, IBM
+ Corporation. Maintained by Jon Masters and others.
+ </para>
+ </refsect1>
+
+ <refsect1><title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/modprobe.d.xml b/man/modprobe.d.xml
new file mode 100644
index 0000000..2bf6537
--- /dev/null
+++ b/man/modprobe.d.xml
@@ -0,0 +1,241 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="modprobe.d">
+ <refentryinfo>
+ <title>modprobe.d</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Jon</firstname>
+ <surname>Masters</surname>
+ <email>jcm@jonmasters.org</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Robby</firstname>
+ <surname>Workman</surname>
+ <email>rworkman@slackware.com</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+
+ <refmeta>
+ <refentrytitle>modprobe.d</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>modprobe.d</refname>
+ <refpurpose>Configuration directory for modprobe</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/lib/modprobe.d/*.conf</filename></para>
+ <para><filename>@DISTCONFDIR@/modprobe.d/*.conf</filename></para>
+ <para><filename>/usr/local/lib/modprobe.d/*.conf</filename></para>
+ <para><filename>/run/modprobe.d/*.conf</filename></para>
+ <para><filename>/etc/modprobe.d/*.conf</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>Because the <command>modprobe</command> command can add or
+ remove more than one module, due to modules having dependencies,
+ we need a method of specifying what options are to be used with
+ those modules. All files underneath the
+ <filename>/etc/modprobe.d</filename> directory which end with the
+ <filename>.conf</filename> extension specify those options as
+ required. They can also be used to create convenient aliases:
+ alternate names for a module, or they can override the normal
+ <command>modprobe</command> behavior altogether for those with
+ special requirements (such as inserting more than one module).
+ </para>
+ <para>
+ Note that module and alias names (like other module names) can
+ have - or _ in them: both are interchangeable throughout all the
+ module commands as underscore conversion happens automatically.
+ </para>
+ <para>
+ The format of files under <filename>modprobe.d</filename> is
+ simple: one command per line, with blank lines and lines starting
+ with '#' ignored (useful for adding comments). A '\' at the end
+ of a line causes it to continue on the next line, which makes the
+ file a bit neater.
+ </para>
+ </refsect1>
+
+ <refsect1><title>COMMANDS</title>
+ <variablelist>
+ <varlistentry>
+ <term>alias <replaceable>wildcard</replaceable> <replaceable>modulename</replaceable>
+ </term>
+ <listitem>
+ <para>
+ This allows you to give alternate names for a module. For example:
+ "alias my-mod really_long_modulename" means you can use "modprobe
+ my-mod" instead of "modprobe really_long_modulename". You can also
+ use shell-style wildcards, so "alias my-mod*
+ really_long_modulename" means that "modprobe my-mod-something" has
+ the same effect. You can't have aliases to other aliases (that way
+ lies madness), but aliases can have options, which will be added to
+ any other options.
+ </para>
+ <para>
+ Note that modules can also contain their own aliases, which you can
+ see using <command>modinfo</command>. These aliases are used as a
+ last resort (ie. if there is no real module,
+ <command>install</command>, <command>remove</command>, or
+ <command>alias</command> command in the configuration).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>blacklist <replaceable>modulename</replaceable>
+ </term>
+ <listitem>
+ <para>
+ Modules can contain their own aliases: usually these are aliases
+ describing the devices they support, such as "pci:123...". These
+ "internal" aliases can be overridden by normal "alias" keywords,
+ but there are cases where two or more modules both support the same
+ devices, or a module invalidly claims to support a device that it
+ does not: the <command>blacklist</command> keyword indicates that
+ all of that particular module's internal aliases are to be ignored.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>install <replaceable>modulename</replaceable> <replaceable>command...</replaceable>
+ </term>
+ <listitem>
+ <para>
+ This command instructs <command>modprobe</command> to run your
+ command instead of inserting the module in the kernel as normal.
+ The command can be any shell command: this allows you to do any
+ kind of complex processing you might wish. For example, if the
+ module "fred" works better with the module "barney" already
+ installed (but it doesn't depend on it, so
+ <command>modprobe</command> won't automatically load it), you could
+ say "install fred /sbin/modprobe barney; /sbin/modprobe
+ --ignore-install fred", which would do what you wanted. Note the
+ <option>--ignore-install</option>, which stops the second
+ <command>modprobe</command> from running the same
+ <command>install</command> command again. See also
+ <command>remove</command> below. </para> <para>The long term
+ future of this command as a solution to the problem of providing
+ additional module dependencies is not assured and it is intended to
+ replace this command with a warning about its eventual removal or
+ deprecation at some point in a future release. Its use complicates
+ the automated determination of module dependencies by distribution
+ utilities, such as mkinitrd (because these now need to somehow
+ interpret what the <command>install</command> commands might be
+ doing. In a perfect world, modules would provide all dependency
+ information without the use of this command and work is underway to
+ implement soft dependency support within the Linux kernel. </para>
+ <para> If you use the string "$CMDLINE_OPTS" in the command, it will
+ be replaced by any options specified on the modprobe command line.
+ This can be useful because users expect "modprobe fred opt=1" to
+ pass the "opt=1" arg to the module, even if there's an install
+ command in the configuration file. So our above example becomes
+ "install fred /sbin/modprobe barney; /sbin/modprobe
+ --ignore-install fred $CMDLINE_OPTS"
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>options <replaceable>modulename</replaceable> <replaceable>option...</replaceable>
+ </term>
+ <listitem>
+ <para>
+ This command allows you to add options to the module
+ <replaceable>modulename</replaceable> (which might be an
+ alias) every time it is inserted into the kernel: whether
+ directly (using <command>modprobe </command>
+ <replaceable>modulename</replaceable>) or because the
+ module being inserted depends on this module.
+ </para>
+ <para>
+ All options are added together: they can come from an
+ <command>option</command> for the module itself, for an
+ alias, and on the command line.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>remove <replaceable>modulename</replaceable> <replaceable>command...</replaceable>
+ </term>
+ <listitem>
+ <para>
+ This is similar to the <command>install</command> command
+ above, except it is invoked when "modprobe -r" is run.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>softdep <replaceable>modulename</replaceable> pre: <replaceable>modules...</replaceable> post: <replaceable>modules...</replaceable>
+ </term>
+ <listitem>
+ <para>
+ The <command>softdep</command> command allows you to specify soft,
+ or optional, module dependencies. <replaceable>modulename</replaceable>
+ can be used without these optional modules installed, but usually with
+ some features missing. For example, a driver for a storage HBA might
+ require another module be loaded in order to use management features.
+ </para>
+ <para>
+ pre-deps and post-deps modules are lists of names and/or aliases of other
+ modules that modprobe will attempt to install (or remove) in order
+ before and after the main module given in the
+ <replaceable>modulename</replaceable> argument.
+ </para>
+ <para>
+ Example: Assume "softdep c pre: a b post: d e" is provided in the
+ configuration. Running "modprobe c" is now equivalent to
+ "modprobe a b c d e" without the softdep.
+ Flags such as --use-blacklist are applied to all the specified
+ modules, while module parameters only apply to module c.
+ </para>
+ <para>
+ Note: if there are <command>install</command> or
+ <command>remove</command> commands with the same
+ <replaceable>modulename</replaceable> argument,
+ <command>softdep</command> takes precedence.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1><title>COMPATIBILITY</title>
+ <para>
+ A future version of kmod will come with a strong warning to avoid use of
+ the <command>install</command> as explained above. This will happen once
+ support for soft dependencies in the kernel is complete. That support
+ will complement the existing softdep support within this utility by
+ providing such dependencies directly within the modules.
+ </para>
+ </refsect1>
+ <refsect1><title>COPYRIGHT</title>
+ <para>
+ This manual page originally Copyright 2004, Rusty Russell, IBM
+ Corporation. Maintained by Jon Masters and others.
+ </para>
+ </refsect1>
+ <refsect1><title>SEE ALSO</title>
+ <para><citerefentry>
+ <refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modules.dep</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/modprobe.xml b/man/modprobe.xml
new file mode 100644
index 0000000..4d1fd59
--- /dev/null
+++ b/man/modprobe.xml
@@ -0,0 +1,544 @@
+<?xml version='1.0'?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="modprobe">
+ <refentryinfo>
+ <title>modprobe</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Jon</firstname>
+ <surname>Masters</surname>
+ <email>jcm@jonmasters.org</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Robby</firstname>
+ <surname>Workman</surname>
+ <email>rworkman@slackware.com</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>modprobe</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>modprobe</refname>
+ <refpurpose>Add and remove modules from the Linux Kernel</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>modprobe</command>
+ <arg><option>-v</option></arg>
+ <arg><option>-V</option></arg>
+ <arg><option>-C <replaceable>config-file</replaceable></option></arg>
+ <arg><option>-n</option></arg>
+ <arg><option>-i</option></arg>
+ <arg><option>-q</option></arg>
+ <arg><option>-b</option></arg>
+ <arg><replaceable>modulename</replaceable></arg>
+ <arg rep='repeat'><option><replaceable>module parameters</replaceable></option></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>modprobe</command>
+ <arg>-r</arg>
+ <arg><option>-v</option></arg>
+ <arg><option>-n</option></arg>
+ <arg><option>-i</option></arg>
+ <arg rep='repeat'><option><replaceable>modulename</replaceable></option></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>modprobe</command>
+ <arg>-c</arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>modprobe</command>
+ <arg>--dump-modversions</arg> <arg><replaceable>filename</replaceable></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>modprobe</command> intelligently adds or removes a
+ module from the Linux kernel: note that for convenience, there
+ is no difference between _ and - in module names (automatic
+ underscore conversion is performed).
+ <command>modprobe</command> looks in the module directory
+ <filename>@MODULE_DIRECTORY@/`uname -r`</filename> for all
+ the modules and other files, except for the optional
+ configuration files in the
+ <filename>/etc/modprobe.d</filename> directory
+ (see <citerefentry>
+ <refentrytitle>modprobe.d</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>). <command>modprobe</command> will also use module
+ options specified on the kernel command line in the form of
+ &lt;module&gt;.&lt;option&gt; and blacklists in the form of
+ modprobe.blacklist=&lt;module&gt;.
+ </para>
+ <para>
+ Note that unlike in 2.4 series Linux kernels (which are not supported
+ by this tool) this version of <command>modprobe</command> does not
+ do anything to the module itself: the work of resolving symbols
+ and understanding parameters is done inside the kernel. So
+ module failure is sometimes accompanied by a kernel message: see
+ <citerefentry>
+ <refentrytitle>dmesg</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>.
+ </para>
+ <para>
+ <command>modprobe</command> expects an up-to-date
+ <filename>modules.dep.bin</filename> file as generated
+ by the corresponding <command>depmod</command> utility shipped
+ along with <command>modprobe</command> (see
+ <citerefentry><refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>). This file lists what other modules each
+ module needs (if any), and <command>modprobe</command> uses this
+ to add or remove these dependencies automatically.
+ </para>
+ <para>
+ If any arguments are given after the
+ <replaceable>modulename</replaceable>, they are passed to the
+ kernel (in addition to any options listed in the configuration
+ file).
+ </para>
+ <para>
+ When loading modules, <replaceable>modulename</replaceable> can also
+ be a path to the module. If the path is relative, it must
+ explicitly start with "./". Note that this may fail when using a
+ path to a module with dependencies not matching the installed depmod
+ database.
+ </para>
+ </refsect1>
+
+ <refsect1><title>OPTIONS</title>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-a</option>
+ </term>
+ <term>
+ <option>--all</option>
+ </term>
+ <listitem>
+ <para>Insert all module names on the command line.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-b</option>
+ </term>
+ <term>
+ <option>--use-blacklist</option>
+ </term>
+ <listitem>
+ <para>
+ This option causes <command>modprobe</command> to apply the
+ <command>blacklist</command> commands in the configuration files
+ (if any) to module names as well. It is usually used by
+ <citerefentry>
+ <refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum>
+ </citerefentry>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-C</option>
+ </term>
+ <term>
+ <option>--config</option>
+ </term>
+ <listitem>
+ <para>This option overrides the default configuration directory
+ (<filename>/etc/modprobe.d</filename>).
+ </para>
+ <para>
+ This option is passed through <command>install</command>
+ or <command>remove</command> commands to other
+ <command>modprobe</command> commands in the
+ MODPROBE_OPTIONS environment variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-c</option>
+ </term>
+ <term>
+ <option>--showconfig</option>
+ </term>
+ <listitem>
+ <para>
+ Dump out the effective configuration from the config directory and
+ exit.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--dump-modversions</option>
+ </term>
+ <listitem>
+ <para>
+ Print out a list of module versioning information required by a
+ module. This option is commonly used by distributions in order to
+ package up a Linux kernel module using module versioning deps.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-d</option>
+ </term>
+ <term>
+ <option>--dirname</option>
+ </term>
+ <listitem>
+ <para>
+ Root directory for modules, <filename>/</filename> by default.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--first-time</option>
+ </term>
+ <listitem>
+ <para>
+ Normally, <command>modprobe</command> will succeed (and do
+ nothing) if told to insert a module which is already
+ present or to remove a module which isn't present. This is
+ ideal for simple scripts; however, more complicated scripts often
+ want to know whether <command>modprobe</command> really
+ did something: this option makes modprobe fail in the
+ case that it actually didn't do anything.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--force-vermagic</option>
+ </term>
+ <listitem>
+ <para>
+ Every module contains a small string containing important
+ information, such as the kernel and compiler versions. If a module
+ fails to load and the kernel complains that the "version magic"
+ doesn't match, you can use this option to remove it. Naturally,
+ this check is there for your protection, so using this option is
+ dangerous unless you know what you're doing.
+ </para>
+ <para>
+ This applies to any modules inserted: both the module (or alias) on
+ the command line and any modules on which it depends.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--force-modversion</option>
+ </term>
+ <listitem>
+ <para>
+ When modules are compiled with CONFIG_MODVERSIONS set, a section
+ detailing the versions of every interfaced used by (or supplied by)
+ the module is created. If a module fails to load and the kernel
+ complains that the module disagrees about a version of some
+ interface, you can use "--force-modversion" to remove the version
+ information altogether. Naturally, this check is there for your
+ protection, so using this option is dangerous unless you know what
+ you're doing.
+ </para>
+ <para>
+ This applies any modules inserted: both the module (or alias) on
+ the command line and any modules on which it depends.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-f</option>
+ </term>
+ <term>
+ <option>--force</option>
+ </term>
+ <listitem>
+ <para>
+ Try to strip any versioning information from the module which might
+ otherwise stop it from loading: this is the same as using both
+ <option>--force-vermagic</option> and
+ <option>--force-modversion</option>. Naturally, these checks are
+ there for your protection, so using this option is dangerous unless
+ you know what you are doing.
+ </para>
+ <para>
+ This applies to any modules inserted: both the module (or alias) on
+ the command line and any modules it on which it depends.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-i</option>
+ </term>
+ <term>
+ <option>--ignore-install</option>
+ </term>
+ <term>
+ <option>--ignore-remove</option>
+ </term>
+ <listitem>
+ <para>
+ This option causes <command>modprobe</command> to ignore
+ <command>install</command> and <command>remove</command> commands
+ in the configuration file (if any) for the module specified on the
+ command line (any dependent modules are still subject to commands
+ set for them in the configuration file). Both
+ <command>install</command> and <command>remove</command> commands
+ will currently be ignored when this option is used regardless of
+ whether the request was more specifically made with only one or
+ other (and not both) of <option>--ignore-install</option> or
+ <option>--ignore-remove</option>. See <citerefentry>
+ <refentrytitle>modprobe.d</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-n</option>
+ </term>
+ <term>
+ <option>--dry-run</option>
+ </term>
+ <term>
+ <option>--show</option>
+ </term>
+ <listitem>
+ <para>
+ This option does everything but actually insert or delete the
+ modules (or run the install or remove commands). Combined with
+ <option>-v</option>, it is useful for debugging problems. For
+ historical reasons both <option>--dry-run</option> and
+ <option>--show</option> actually mean the same thing and are
+ interchangeable.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-q</option>
+ </term>
+ <term>
+ <option>--quiet</option>
+ </term>
+ <listitem>
+ <para>
+ With this flag, <command>modprobe</command> won't print an error
+ message if you try to remove or insert a module it can't find (and
+ isn't an alias or
+ <command>install</command>/<command>remove</command> command).
+ However, it will still return with a non-zero exit status. The
+ kernel uses this to opportunistically probe for modules which might
+ exist using request_module.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-R</option>
+ </term>
+ <term>
+ <option>--resolve-alias</option>
+ </term>
+ <listitem>
+ <para>
+ Print all module names matching an alias. This can be useful for
+ debugging module alias problems.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-r</option>
+ </term>
+ <term>
+ <option>--remove</option>
+ </term>
+ <listitem>
+ <para>
+ This option causes <command>modprobe</command> to remove rather
+ than insert a module. If the modules it depends on are also
+ unused, <command>modprobe</command> will try to remove them too.
+ Unlike insertion, more than one module can be specified on the
+ command line (it does not make sense to specify module parameters
+ when removing modules).
+ </para>
+ <para>
+ There is usually no reason to remove modules, but some buggy
+ modules require it. Your distribution kernel may not have been
+ built to support removal of modules at all.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-w</option>
+ </term>
+ <term>
+ <option>--wait=</option>TIMEOUT_MSEC
+ </term>
+ <listitem>
+ <para>
+ This option causes <command>modprobe -r</command> to continue trying to
+ remove a module if it fails due to the module being busy, i.e. its refcount
+ is not 0 at the time the call is made. Modprobe tries to remove the module
+ with an incremental sleep time between each tentative up until the maximum
+ wait time in milliseconds passed in this option.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-S</option>
+ </term>
+ <term>
+ <option>--set-version</option>
+ </term>
+ <listitem>
+ <para>
+ Set the kernel version, rather than using
+ <citerefentry><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+ to decide on the kernel version (which dictates where to find the
+ modules).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>--show-depends</option>
+ </term>
+ <listitem>
+ <para>
+ List the dependencies of a module (or alias), including the module
+ itself. This produces a (possibly empty) set of module filenames,
+ one per line, each starting with "insmod" and is typically used by
+ distributions to determine which modules to include when generating
+ initrd/initramfs images. <command>Install</command> commands which
+ apply are shown prefixed by "install". It does not run any of the
+ install commands. Note that
+ <citerefentry><refentrytitle>modinfo</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ can be used to extract dependencies of a module from the module
+ itself, but knows nothing of aliases or install commands.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-s</option>
+ </term>
+ <term>
+ <option>--syslog</option>
+ </term>
+ <listitem>
+ <para>
+ This option causes any error messages to go through the syslog
+ mechanism (as LOG_DAEMON with level LOG_NOTICE) rather than to
+ standard error. This is also automatically enabled when stderr is
+ unavailable.
+ </para>
+ <para>
+ This option is passed through <command>install</command> or
+ <command>remove</command> commands to other
+ <command>modprobe</command> commands in the MODPROBE_OPTIONS
+ environment variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-V</option>
+ </term>
+ <term>
+ <option>--version</option>
+ </term>
+ <listitem>
+ <para>Show version of program and exit.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-v</option>
+ </term>
+ <term>
+ <option>--verbose</option>
+ </term>
+ <listitem>
+ <para>
+ Print messages about what the program is doing. Usually
+ <command>modprobe</command> only prints messages if something goes
+ wrong.
+ </para>
+ <para>
+ This option is passed through <command>install</command> or
+ <command>remove</command> commands to other
+ <command>modprobe</command> commands in the MODPROBE_OPTIONS
+ environment variable.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1><title>ENVIRONMENT</title>
+ <para>
+ The MODPROBE_OPTIONS environment variable can also be used to pass
+ arguments to <command>modprobe</command>.
+ </para>
+ </refsect1>
+
+ <refsect1><title>COPYRIGHT</title>
+ <para>
+ This manual page originally Copyright 2002, Rusty Russell, IBM
+ Corporation. Maintained by Jon Masters and others.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>modprobe.d</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>insmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>rmmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>lsmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modinfo</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ <citerefentry>
+ <refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/modules.dep.xml b/man/modules.dep.xml
new file mode 100644
index 0000000..8ef6d8b
--- /dev/null
+++ b/man/modules.dep.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="modules.dep">
+ <refentryinfo>
+ <title>modules.dep</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Jon</firstname>
+ <surname>Masters</surname>
+ <email>jcm@jonmasters.org</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>modules.dep</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>modules.dep</refname>
+ <refname>modules.dep.bin</refname>
+ <refpurpose>Module dependency information</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>@MODULE_DIRECTORY@/modules.dep</filename></para>
+ <para><filename>@MODULE_DIRECTORY@/modules.dep.bin</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>
+ <filename>modules.dep.bin</filename> is a binary file generated by
+ <command>depmod</command> listing the dependencies for
+ every module in the directories under
+ <filename>@MODULE_DIRECTORY@/</filename><replaceable>version</replaceable>.
+ It is used by kmod tools such as <command>modprobe</command> and
+ libkmod.
+ </para>
+ <para>
+ Its text counterpart is located in the same directory with the name
+ <filename>modules.dep</filename>. The text version is maintained only
+ for easy of reading by humans and is in no way used by any kmod tool.
+ </para>
+ <para>
+ These files are not intended for editing or use by any additional
+ utilities as their format is subject to change in the future. You should
+ use the
+ <citerefentry><refentrytitle>modinfo</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ command to obtain information about modules in a future proof and
+ compatible fashion rather than touching these files.
+ </para>
+ </refsect1>
+ <refsect1><title>COPYRIGHT</title>
+ <para>
+ This manual page originally Copyright 2002, Rusty Russell, IBM
+ Corporation. Maintained by Jon Masters and others.
+ </para>
+ </refsect1>
+ <refsect1><title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/rmmod.xml b/man/rmmod.xml
new file mode 100644
index 0000000..67bcbed
--- /dev/null
+++ b/man/rmmod.xml
@@ -0,0 +1,148 @@
+<?xml version='1.0'?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="rmmod">
+ <refentryinfo>
+ <title>rmmod</title>
+ <productname>kmod</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Jon</firstname>
+ <surname>Masters</surname>
+ <email>jcm@jonmasters.org</email>
+ </author>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lucas</firstname>
+ <surname>De Marchi</surname>
+ <email>lucas.de.marchi@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>rmmod</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>rmmod</refname>
+ <refpurpose>
+ Simple program to remove a module from the Linux Kernel
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>rmmod</command>
+ <arg><option>-f</option></arg>
+ <arg><option>-s</option></arg>
+ <arg><option>-v</option></arg>
+ <arg><replaceable>modulename</replaceable></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>
+ <command>rmmod</command> is a trivial program to remove a module (when
+ module unloading support is provided) from the kernel. Most users will
+ want to use
+ <citerefentry>
+ <refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry> with the <option>-r</option> option instead
+ since it removes unused dependent modules as well.
+ </para>
+ </refsect1>
+
+ <refsect1><title>OPTIONS</title>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-v</option>
+ </term>
+ <term>
+ <option>--verbose</option>
+ </term>
+ <listitem>
+ <para>
+ Print messages about what the program is doing.
+ Usually <command>rmmod</command> prints messages
+ only if something goes wrong.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-f</option>
+ </term>
+ <term>
+ <option>--force</option>
+ </term>
+ <listitem>
+ <para>
+ This option can be extremely dangerous: it has no effect unless
+ CONFIG_MODULE_FORCE_UNLOAD was set when the kernel was compiled.
+ With this option, you can remove modules which are being used, or
+ which are not designed to be removed, or have been marked as unsafe
+ (see <citerefentry>
+ <refentrytitle>lsmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-s</option>
+ </term>
+ <term>
+ <option>--syslog</option>
+ </term>
+ <listitem>
+ <para>
+ Send errors to syslog instead of standard error.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-V</option> <option>--version</option>
+ </term>
+ <listitem>
+ <para>
+ Show version of program and exit.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>COPYRIGHT</title>
+ <para>
+ This manual page originally Copyright 2002, Rusty Russell, IBM
+ Corporation. Maintained by Jon Masters and others.
+ </para>
+ </refsect1>
+
+ <refsect1><title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>modprobe</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>insmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>lsmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>modinfo</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ <citerefentry>
+ <refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/shared/.gitignore b/shared/.gitignore
new file mode 100644
index 0000000..088ef79
--- /dev/null
+++ b/shared/.gitignore
@@ -0,0 +1,5 @@
+.dirstamp
+.deps/
+.libs/
+*.la
+*.lo
diff --git a/shared/array.c b/shared/array.c
new file mode 100644
index 0000000..c2e2e14
--- /dev/null
+++ b/shared/array.c
@@ -0,0 +1,113 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/array.h>
+
+/* basic pointer array growing in steps */
+
+
+static int array_realloc(struct array *array, size_t new_total)
+{
+ void *tmp = realloc(array->array, sizeof(void *) * new_total);
+ if (tmp == NULL)
+ return -ENOMEM;
+ array->array = tmp;
+ array->total = new_total;
+ return 0;
+}
+
+void array_init(struct array *array, size_t step)
+{
+ assert(step > 0);
+ array->array = NULL;
+ array->count = 0;
+ array->total = 0;
+ array->step = step;
+}
+
+int array_append(struct array *array, const void *element)
+{
+ size_t idx;
+
+ if (array->count + 1 >= array->total) {
+ int r = array_realloc(array, array->total + array->step);
+ if (r < 0)
+ return r;
+ }
+ idx = array->count;
+ array->array[idx] = (void *)element;
+ array->count++;
+ return idx;
+}
+
+int array_append_unique(struct array *array, const void *element)
+{
+ void **itr = array->array;
+ void **itr_end = itr + array->count;
+ for (; itr < itr_end; itr++)
+ if (*itr == element)
+ return -EEXIST;
+ return array_append(array, element);
+}
+
+void array_pop(struct array *array) {
+ array->count--;
+ if (array->count + array->step < array->total) {
+ int r = array_realloc(array, array->total - array->step);
+ if (r < 0)
+ return;
+ }
+}
+
+void array_free_array(struct array *array) {
+ free(array->array);
+ array->count = 0;
+ array->total = 0;
+}
+
+
+void array_sort(struct array *array, int (*cmp)(const void *a, const void *b))
+{
+ qsort(array->array, array->count, sizeof(void *), cmp);
+}
+
+int array_remove_at(struct array *array, unsigned int pos)
+{
+ if (array->count <= pos)
+ return -ENOENT;
+
+ array->count--;
+ if (pos < array->count)
+ memmove(array->array + pos, array->array + pos + 1,
+ sizeof(void *) * (array->count - pos));
+
+ if (array->count + array->step < array->total) {
+ int r = array_realloc(array, array->total - array->step);
+ /* ignore error */
+ if (r < 0)
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/shared/array.h b/shared/array.h
new file mode 100644
index 0000000..b88482f
--- /dev/null
+++ b/shared/array.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <stddef.h>
+
+/*
+ * Declaration of struct array is in header because we may want to embed the
+ * structure into another, so we need to know its size
+ */
+struct array {
+ void **array;
+ size_t count;
+ size_t total;
+ size_t step;
+};
+
+void array_init(struct array *array, size_t step);
+int array_append(struct array *array, const void *element);
+int array_append_unique(struct array *array, const void *element);
+void array_pop(struct array *array);
+void array_free_array(struct array *array);
+void array_sort(struct array *array, int (*cmp)(const void *a, const void *b));
+int array_remove_at(struct array *array, unsigned int pos);
diff --git a/shared/hash.c b/shared/hash.c
new file mode 100644
index 0000000..a87bc50
--- /dev/null
+++ b/shared/hash.c
@@ -0,0 +1,341 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/hash.h>
+#include <shared/util.h>
+
+struct hash_entry {
+ const char *key;
+ const void *value;
+};
+
+struct hash_bucket {
+ struct hash_entry *entries;
+ unsigned int used;
+ unsigned int total;
+};
+
+struct hash {
+ unsigned int count;
+ unsigned int step;
+ unsigned int n_buckets;
+ void (*free_value)(void *value);
+ struct hash_bucket buckets[];
+};
+
+struct hash *hash_new(unsigned int n_buckets,
+ void (*free_value)(void *value))
+{
+ struct hash *hash;
+
+ n_buckets = ALIGN_POWER2(n_buckets);
+ hash = calloc(1, sizeof(struct hash) +
+ n_buckets * sizeof(struct hash_bucket));
+ if (hash == NULL)
+ return NULL;
+ hash->n_buckets = n_buckets;
+ hash->free_value = free_value;
+ hash->step = n_buckets / 32;
+ if (hash->step == 0)
+ hash->step = 4;
+ else if (hash->step > 64)
+ hash->step = 64;
+ return hash;
+}
+
+void hash_free(struct hash *hash)
+{
+ struct hash_bucket *bucket, *bucket_end;
+
+ if (hash == NULL)
+ return;
+
+ bucket = hash->buckets;
+ bucket_end = bucket + hash->n_buckets;
+ for (; bucket < bucket_end; bucket++) {
+ if (hash->free_value) {
+ struct hash_entry *entry, *entry_end;
+ entry = bucket->entries;
+ entry_end = entry + bucket->used;
+ for (; entry < entry_end; entry++)
+ hash->free_value((void *)entry->value);
+ }
+ free(bucket->entries);
+ }
+ free(hash);
+}
+
+static inline unsigned int hash_superfast(const char *key, unsigned int len)
+{
+ /* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html)
+ * used by WebCore (http://webkit.org/blog/8/hashtables-part-2/)
+ * EFL's eina and possible others.
+ */
+ unsigned int tmp, hash = len, rem = len & 3;
+
+ len /= 4;
+
+ /* Main loop */
+ for (; len > 0; len--) {
+ hash += get_unaligned((uint16_t *) key);
+ tmp = (get_unaligned((uint16_t *)(key + 2)) << 11) ^ hash;
+ hash = (hash << 16) ^ tmp;
+ key += 4;
+ hash += hash >> 11;
+ }
+
+ /* Handle end cases */
+ switch (rem) {
+ case 3:
+ hash += get_unaligned((uint16_t *) key);
+ hash ^= hash << 16;
+ hash ^= key[2] << 18;
+ hash += hash >> 11;
+ break;
+
+ case 2:
+ hash += get_unaligned((uint16_t *) key);
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ break;
+
+ case 1:
+ hash += *key;
+ hash ^= hash << 10;
+ hash += hash >> 1;
+ }
+
+ /* Force "avalanching" of final 127 bits */
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+
+ return hash;
+}
+
+/*
+ * add or replace key in hash map.
+ *
+ * none of key or value are copied, just references are remembered as is,
+ * make sure they are live while pair exists in hash!
+ */
+int hash_add(struct hash *hash, const char *key, const void *value)
+{
+ unsigned int keylen = strlen(key);
+ unsigned int hashval = hash_superfast(key, keylen);
+ unsigned int pos = hashval & (hash->n_buckets - 1);
+ struct hash_bucket *bucket = hash->buckets + pos;
+ struct hash_entry *entry, *entry_end;
+
+ if (bucket->used + 1 >= bucket->total) {
+ unsigned new_total = bucket->total + hash->step;
+ size_t size = new_total * sizeof(struct hash_entry);
+ struct hash_entry *tmp = realloc(bucket->entries, size);
+ if (tmp == NULL)
+ return -errno;
+ bucket->entries = tmp;
+ bucket->total = new_total;
+ }
+
+ entry = bucket->entries;
+ entry_end = entry + bucket->used;
+ for (; entry < entry_end; entry++) {
+ int c = strcmp(key, entry->key);
+ if (c == 0) {
+ if (hash->free_value)
+ hash->free_value((void *)entry->value);
+ entry->key = key;
+ entry->value = value;
+ return 0;
+ } else if (c < 0) {
+ memmove(entry + 1, entry,
+ (entry_end - entry) * sizeof(struct hash_entry));
+ break;
+ }
+ }
+
+ entry->key = key;
+ entry->value = value;
+ bucket->used++;
+ hash->count++;
+ return 0;
+}
+
+/* similar to hash_add(), but fails if key already exists */
+int hash_add_unique(struct hash *hash, const char *key, const void *value)
+{
+ unsigned int keylen = strlen(key);
+ unsigned int hashval = hash_superfast(key, keylen);
+ unsigned int pos = hashval & (hash->n_buckets - 1);
+ struct hash_bucket *bucket = hash->buckets + pos;
+ struct hash_entry *entry, *entry_end;
+
+ if (bucket->used + 1 >= bucket->total) {
+ unsigned new_total = bucket->total + hash->step;
+ size_t size = new_total * sizeof(struct hash_entry);
+ struct hash_entry *tmp = realloc(bucket->entries, size);
+ if (tmp == NULL)
+ return -errno;
+ bucket->entries = tmp;
+ bucket->total = new_total;
+ }
+
+ entry = bucket->entries;
+ entry_end = entry + bucket->used;
+ for (; entry < entry_end; entry++) {
+ int c = strcmp(key, entry->key);
+ if (c == 0)
+ return -EEXIST;
+ else if (c < 0) {
+ memmove(entry + 1, entry,
+ (entry_end - entry) * sizeof(struct hash_entry));
+ break;
+ }
+ }
+
+ entry->key = key;
+ entry->value = value;
+ bucket->used++;
+ hash->count++;
+ return 0;
+}
+
+static int hash_entry_cmp(const void *pa, const void *pb)
+{
+ const struct hash_entry *a = pa;
+ const struct hash_entry *b = pb;
+ return strcmp(a->key, b->key);
+}
+
+void *hash_find(const struct hash *hash, const char *key)
+{
+ unsigned int keylen = strlen(key);
+ unsigned int hashval = hash_superfast(key, keylen);
+ unsigned int pos = hashval & (hash->n_buckets - 1);
+ const struct hash_bucket *bucket = hash->buckets + pos;
+ const struct hash_entry se = {
+ .key = key,
+ .value = NULL
+ };
+ const struct hash_entry *entry;
+
+ if (!bucket->entries)
+ return NULL;
+
+ entry = bsearch(&se, bucket->entries, bucket->used,
+ sizeof(struct hash_entry), hash_entry_cmp);
+
+ return entry ? (void *)entry->value : NULL;
+}
+
+int hash_del(struct hash *hash, const char *key)
+{
+ unsigned int keylen = strlen(key);
+ unsigned int hashval = hash_superfast(key, keylen);
+ unsigned int pos = hashval & (hash->n_buckets - 1);
+ unsigned int steps_used, steps_total;
+ struct hash_bucket *bucket = hash->buckets + pos;
+ struct hash_entry *entry, *entry_end;
+ const struct hash_entry se = {
+ .key = key,
+ .value = NULL
+ };
+
+ entry = bsearch(&se, bucket->entries, bucket->used,
+ sizeof(struct hash_entry), hash_entry_cmp);
+ if (entry == NULL)
+ return -ENOENT;
+
+ if (hash->free_value)
+ hash->free_value((void *)entry->value);
+
+ entry_end = bucket->entries + bucket->used;
+ memmove(entry, entry + 1,
+ (entry_end - entry) * sizeof(struct hash_entry));
+
+ bucket->used--;
+ hash->count--;
+
+ steps_used = bucket->used / hash->step;
+ steps_total = bucket->total / hash->step;
+ if (steps_used + 1 < steps_total) {
+ size_t size = (steps_used + 1) *
+ hash->step * sizeof(struct hash_entry);
+ struct hash_entry *tmp = realloc(bucket->entries, size);
+ if (tmp) {
+ bucket->entries = tmp;
+ bucket->total = (steps_used + 1) * hash->step;
+ }
+ }
+
+ return 0;
+}
+
+unsigned int hash_get_count(const struct hash *hash)
+{
+ return hash->count;
+}
+
+void hash_iter_init(const struct hash *hash, struct hash_iter *iter)
+{
+ iter->hash = hash;
+ iter->bucket = 0;
+ iter->entry = -1;
+}
+
+bool hash_iter_next(struct hash_iter *iter, const char **key,
+ const void **value)
+{
+ const struct hash_bucket *b = iter->hash->buckets + iter->bucket;
+ const struct hash_entry *e;
+
+ iter->entry++;
+
+ if (iter->entry >= b->used) {
+ iter->entry = 0;
+
+ for (iter->bucket++; iter->bucket < iter->hash->n_buckets;
+ iter->bucket++) {
+ b = iter->hash->buckets + iter->bucket;
+
+ if (b->used > 0)
+ break;
+ }
+
+ if (iter->bucket >= iter->hash->n_buckets)
+ return false;
+ }
+
+ e = b->entries + iter->entry;
+
+ if (value != NULL)
+ *value = e->value;
+ if (key != NULL)
+ *key = e->key;
+
+ return true;
+}
diff --git a/shared/hash.h b/shared/hash.h
new file mode 100644
index 0000000..ca0af05
--- /dev/null
+++ b/shared/hash.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <stdbool.h>
+
+struct hash;
+
+struct hash_iter {
+ const struct hash *hash;
+ unsigned int bucket;
+ unsigned int entry;
+};
+
+struct hash *hash_new(unsigned int n_buckets, void (*free_value)(void *value));
+void hash_free(struct hash *hash);
+int hash_add(struct hash *hash, const char *key, const void *value);
+int hash_add_unique(struct hash *hash, const char *key, const void *value);
+int hash_del(struct hash *hash, const char *key);
+void *hash_find(const struct hash *hash, const char *key);
+unsigned int hash_get_count(const struct hash *hash);
+void hash_iter_init(const struct hash *hash, struct hash_iter *iter);
+bool hash_iter_next(struct hash_iter *iter, const char **key,
+ const void **value);
diff --git a/shared/macro.h b/shared/macro.h
new file mode 100644
index 0000000..b59f7dc
--- /dev/null
+++ b/shared/macro.h
@@ -0,0 +1,76 @@
+/*
+ * kmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <stddef.h>
+
+#if defined(HAVE_STATIC_ASSERT)
+#define assert_cc(expr) \
+ _Static_assert((expr), #expr)
+#else
+#define assert_cc(expr) \
+ do { (void) sizeof(char [1 - 2*!(expr)]); } while(0)
+#endif
+
+#define check_types_match(expr1, expr2) \
+ ((typeof(expr1) *)0 != (typeof(expr2) *)0)
+
+#define container_of(member_ptr, containing_type, member) \
+ ((containing_type *) \
+ ((char *)(member_ptr) - offsetof(containing_type, member)) \
+ - check_types_match(*(member_ptr), ((containing_type *)0)->member))
+
+
+/* Two gcc extensions.
+ * &a[0] degrades to a pointer: a different type from an array */
+#define _array_size_chk(arr) ({ \
+ assert_cc(!__builtin_types_compatible_p(typeof(arr), typeof(&(arr)[0]))); \
+ 0; \
+ })
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + _array_size_chk(arr))
+
+#define XSTRINGIFY(x) #x
+#define STRINGIFY(x) XSTRINGIFY(x)
+
+#define XCONCATENATE(x, y) x ## y
+#define CONCATENATE(x, y) XCONCATENATE(x, y)
+#define UNIQ(x) CONCATENATE(x, __COUNTER__)
+
+/* Temporaries for importing index handling */
+#define NOFAIL(x) (x)
+#define fatal(x...) do { } while (0)
+
+/* Attributes */
+
+#define _must_check_ __attribute__((warn_unused_result))
+#define _printf_format_(a,b) __attribute__((format (printf, a, b)))
+#define _unused_ __attribute__((unused))
+#define _always_inline_ __inline__ __attribute__((always_inline))
+#define _cleanup_(x) __attribute__((cleanup(x)))
+
+/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
+ * compiler versions */
+#ifndef noreturn
+#if defined(HAVE_NORETURN)
+#define noreturn _Noreturn
+#else
+#define noreturn __attribute__((noreturn))
+#endif
+#endif
diff --git a/shared/missing.h b/shared/missing.h
new file mode 100644
index 0000000..2629444
--- /dev/null
+++ b/shared/missing.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#ifdef HAVE_LINUX_MODULE_H
+#include <linux/module.h>
+#endif
+
+#ifndef MODULE_INIT_IGNORE_MODVERSIONS
+# define MODULE_INIT_IGNORE_MODVERSIONS 1
+#endif
+
+#ifndef MODULE_INIT_IGNORE_VERMAGIC
+# define MODULE_INIT_IGNORE_VERMAGIC 2
+#endif
+
+#ifndef MODULE_INIT_COMPRESSED_FILE
+# define MODULE_INIT_COMPRESSED_FILE 4
+#endif
+
+#ifndef __NR_finit_module
+# define __NR_finit_module -1
+#endif
+
+#ifndef HAVE_FINIT_MODULE
+#include <errno.h>
+
+static inline int finit_module(int fd, const char *uargs, int flags)
+{
+ if (__NR_finit_module == -1) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return syscall(__NR_finit_module, fd, uargs, flags);
+}
+#endif
+
+#if !HAVE_DECL_STRNDUPA
+#define strndupa(s, n) \
+ ({ \
+ const char *__old = (s); \
+ size_t __len = strnlen(__old, (n)); \
+ char *__new = alloca(__len + 1); \
+ __new[__len] = '\0'; \
+ memcpy(__new, __old, __len); \
+ })
+#endif
+
+#if !HAVE_DECL_BE32TOH
+#include <endian.h>
+#include <byteswap.h>
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define be32toh(x) bswap_32 (x)
+#else
+#define be32toh(x) (x)
+#endif
+#endif
diff --git a/shared/scratchbuf.c b/shared/scratchbuf.c
new file mode 100644
index 0000000..8d9eb83
--- /dev/null
+++ b/shared/scratchbuf.c
@@ -0,0 +1,60 @@
+/*
+ * kmod - interface to kernel module operations
+ *
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include "scratchbuf.h"
+
+#include <errno.h>
+#include <string.h>
+
+void scratchbuf_init(struct scratchbuf *buf, char *stackbuf, size_t size)
+{
+ buf->bytes = stackbuf;
+ buf->size = size;
+ buf->need_free = false;
+}
+
+int scratchbuf_alloc(struct scratchbuf *buf, size_t size)
+{
+ char *tmp;
+
+ if (size <= buf->size)
+ return 0;
+
+ if (buf->need_free) {
+ tmp = realloc(buf->bytes, size);
+ if (tmp == NULL)
+ return -ENOMEM;
+ } else {
+ tmp = malloc(size);
+ if (tmp == NULL)
+ return -ENOMEM;
+ memcpy(tmp, buf->bytes, buf->size);
+ }
+
+ buf->size = size;
+ buf->bytes = tmp;
+ buf->need_free = true;
+
+ return 0;
+}
+
+void scratchbuf_release(struct scratchbuf *buf)
+{
+ if (buf->need_free)
+ free(buf->bytes);
+}
diff --git a/shared/scratchbuf.h b/shared/scratchbuf.h
new file mode 100644
index 0000000..27ea9d9
--- /dev/null
+++ b/shared/scratchbuf.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <shared/macro.h>
+
+/*
+ * Buffer abstract data type
+ */
+struct scratchbuf {
+ char *bytes;
+ size_t size;
+ bool need_free;
+};
+
+void scratchbuf_init(struct scratchbuf *buf, char *stackbuf, size_t size);
+int scratchbuf_alloc(struct scratchbuf *buf, size_t sz);
+void scratchbuf_release(struct scratchbuf *buf);
+
+/* Return a C string */
+static inline char *scratchbuf_str(struct scratchbuf *buf)
+{
+ return buf->bytes;
+}
+
+#define SCRATCHBUF_INITIALIZER(buf_) { \
+ .bytes = buf_, \
+ .size = sizeof(buf_) + _array_size_chk(buf_), \
+ .need_free = false, \
+}
diff --git a/shared/strbuf.c b/shared/strbuf.c
new file mode 100644
index 0000000..a69d914
--- /dev/null
+++ b/shared/strbuf.c
@@ -0,0 +1,128 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+#include "strbuf.h"
+
+#define BUF_STEP 128
+
+static bool buf_grow(struct strbuf *buf, size_t newsize)
+{
+ void *tmp;
+ size_t sz;
+
+ if (newsize <= buf->size)
+ return true;
+
+ if (newsize % BUF_STEP == 0)
+ sz = newsize;
+ else
+ sz = ((newsize / BUF_STEP) + 1) * BUF_STEP;
+
+ tmp = realloc(buf->bytes, sz);
+ if (sz > 0 && tmp == NULL)
+ return false;
+ buf->bytes = tmp;
+ buf->size = sz;
+ return true;
+}
+
+void strbuf_init(struct strbuf *buf)
+{
+ buf->bytes = NULL;
+ buf->size = 0;
+ buf->used = 0;
+}
+
+void strbuf_release(struct strbuf *buf)
+{
+ free(buf->bytes);
+}
+
+char *strbuf_steal(struct strbuf *buf)
+{
+ char *bytes;
+
+ bytes = realloc(buf->bytes, buf->used + 1);
+ if (!bytes) {
+ free(buf->bytes);
+ return NULL;
+ }
+ bytes[buf->used] = '\0';
+ return bytes;
+}
+
+const char *strbuf_str(struct strbuf *buf)
+{
+ if (!buf_grow(buf, buf->used + 1))
+ return NULL;
+ buf->bytes[buf->used] = '\0';
+ return buf->bytes;
+}
+
+bool strbuf_pushchar(struct strbuf *buf, char ch)
+{
+ if (!buf_grow(buf, buf->used + 1))
+ return false;
+ buf->bytes[buf->used] = ch;
+ buf->used++;
+ return true;
+}
+
+unsigned strbuf_pushchars(struct strbuf *buf, const char *str)
+{
+ unsigned int len;
+
+ assert(str != NULL);
+ assert(buf != NULL);
+
+ len = strlen(str);
+
+ if (!buf_grow(buf, buf->used + len))
+ return 0;
+
+ memcpy(buf->bytes + buf->used, str, len);
+ buf->used += len;
+
+ return len;
+}
+
+void strbuf_popchar(struct strbuf *buf)
+{
+ assert(buf->used > 0);
+ buf->used--;
+}
+
+void strbuf_popchars(struct strbuf *buf, unsigned n)
+{
+ assert(buf->used >= n);
+ buf->used -= n;
+}
+
+void strbuf_clear(struct strbuf *buf)
+{
+ buf->used = 0;
+}
+
diff --git a/shared/strbuf.h b/shared/strbuf.h
new file mode 100644
index 0000000..0f7ceb1
--- /dev/null
+++ b/shared/strbuf.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <stdbool.h>
+
+/*
+ * Buffer abstract data type
+ */
+struct strbuf {
+ char *bytes;
+ unsigned size;
+ unsigned used;
+};
+
+void strbuf_init(struct strbuf *buf);
+void strbuf_release(struct strbuf *buf);
+void strbuf_clear(struct strbuf *buf);
+
+/* Destroy buffer and return a copy as a C string */
+char *strbuf_steal(struct strbuf *buf);
+
+/*
+ * Return a C string owned by the buffer invalidated if the buffer is
+ * changed).
+ */
+const char *strbuf_str(struct strbuf *buf);
+
+bool strbuf_pushchar(struct strbuf *buf, char ch);
+unsigned strbuf_pushchars(struct strbuf *buf, const char *str);
+void strbuf_popchar(struct strbuf *buf);
+void strbuf_popchars(struct strbuf *buf, unsigned n);
diff --git a/shared/util.c b/shared/util.c
new file mode 100644
index 0000000..e2bab83
--- /dev/null
+++ b/shared/util.c
@@ -0,0 +1,548 @@
+/*
+ * kmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ * Copyright (C) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
+ * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <shared/missing.h>
+#include <shared/util.h>
+
+#define USEC_PER_SEC 1000000ULL
+#define NSEC_PER_USEC 1000ULL
+
+static const struct kmod_ext {
+ const char *ext;
+ size_t len;
+} kmod_exts[] = {
+ {KMOD_EXTENSION_UNCOMPRESSED, sizeof(KMOD_EXTENSION_UNCOMPRESSED) - 1},
+#ifdef ENABLE_ZLIB
+ {".ko.gz", sizeof(".ko.gz") - 1},
+#endif
+#ifdef ENABLE_XZ
+ {".ko.xz", sizeof(".ko.xz") - 1},
+#endif
+#ifdef ENABLE_ZSTD
+ {".ko.zst", sizeof(".ko.zst") - 1},
+#endif
+ { }
+};
+
+/* string handling functions and memory allocations */
+/* ************************************************************************ */
+
+void *memdup(const void *p, size_t n)
+{
+ void *r = malloc(n);
+
+ if (r == NULL)
+ return NULL;
+
+ return memcpy(r, p, n);
+}
+
+char *strchr_replace(char *s, char c, char r)
+{
+ char *p;
+
+ for (p = s; *p != '\0'; p++) {
+ if (*p == c)
+ *p = r;
+ }
+
+ return s;
+}
+
+/* module-related functions */
+/* ************************************************************************ */
+int alias_normalize(const char *alias, char buf[static PATH_MAX], size_t *len)
+{
+ size_t i;
+
+ for (i = 0; i < PATH_MAX - 1; i++) {
+ const char c = alias[i];
+ switch (c) {
+ case '-':
+ buf[i] = '_';
+ break;
+ case ']':
+ return -EINVAL;
+ case '[':
+ while (alias[i] != ']' && alias[i] != '\0') {
+ buf[i] = alias[i];
+ i++;
+ }
+
+ if (alias[i] != ']')
+ return -EINVAL;
+
+ buf[i] = alias[i];
+ break;
+ case '\0':
+ goto finish;
+ default:
+ buf[i] = c;
+ }
+ }
+
+finish:
+ buf[i] = '\0';
+ if (len)
+ *len = i;
+
+ return 0;
+}
+
+/*
+ * Replace dashes with underscores.
+ * Dashes inside character range patterns (e.g. [0-9]) are left unchanged.
+ *
+ * For convenience, it returns error if @s is NULL
+ */
+int underscores(char *s)
+{
+ unsigned int i;
+
+ if (!s)
+ return -EINVAL;
+
+ for (i = 0; s[i]; i++) {
+ switch (s[i]) {
+ case '-':
+ s[i] = '_';
+ break;
+ case ']':
+ return -EINVAL;
+ case '[':
+ i += strcspn(&s[i], "]");
+ if (!s[i])
+ return -EINVAL;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+char *modname_normalize(const char *modname, char buf[static PATH_MAX], size_t *len)
+{
+ size_t s;
+
+ for (s = 0; s < PATH_MAX - 1; s++) {
+ const char c = modname[s];
+ if (c == '-')
+ buf[s] = '_';
+ else if (c == '\0' || c == '.')
+ break;
+ else
+ buf[s] = c;
+ }
+
+ buf[s] = '\0';
+
+ if (len)
+ *len = s;
+
+ return buf;
+}
+
+char *path_to_modname(const char *path, char buf[static PATH_MAX], size_t *len)
+{
+ char *modname;
+
+ modname = basename(path);
+ if (modname == NULL || modname[0] == '\0')
+ return NULL;
+
+ return modname_normalize(modname, buf, len);
+}
+
+bool path_ends_with_kmod_ext(const char *path, size_t len)
+{
+ const struct kmod_ext *eitr;
+
+ for (eitr = kmod_exts; eitr->ext != NULL; eitr++) {
+ if (len <= eitr->len)
+ continue;
+ if (streq(path + len - eitr->len, eitr->ext))
+ return true;
+ }
+
+ return false;
+}
+
+/* read-like and fread-like functions */
+/* ************************************************************************ */
+ssize_t read_str_safe(int fd, char *buf, size_t buflen)
+{
+ size_t todo = buflen - 1;
+ size_t done = 0;
+
+ assert_cc(EAGAIN == EWOULDBLOCK);
+
+ do {
+ ssize_t r = read(fd, buf + done, todo);
+
+ if (r == 0)
+ break;
+ else if (r > 0) {
+ todo -= r;
+ done += r;
+ } else {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ else
+ return -errno;
+ }
+ } while (todo > 0);
+
+ buf[done] = '\0';
+ return done;
+}
+
+ssize_t write_str_safe(int fd, const char *buf, size_t buflen)
+{
+ size_t todo = buflen;
+ size_t done = 0;
+
+ assert_cc(EAGAIN == EWOULDBLOCK);
+
+ do {
+ ssize_t r = write(fd, buf + done, todo);
+
+ if (r == 0)
+ break;
+ else if (r > 0) {
+ todo -= r;
+ done += r;
+ } else {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ else
+ return -errno;
+ }
+ } while (todo > 0);
+
+ return done;
+}
+
+int read_str_long(int fd, long *value, int base)
+{
+ char buf[32], *end;
+ long v;
+ int err;
+
+ *value = 0;
+ err = read_str_safe(fd, buf, sizeof(buf));
+ if (err < 0)
+ return err;
+ errno = 0;
+ v = strtol(buf, &end, base);
+ if (end == buf || !isspace(*end))
+ return -EINVAL;
+
+ *value = v;
+ return 0;
+}
+
+int read_str_ulong(int fd, unsigned long *value, int base)
+{
+ char buf[32], *end;
+ long v;
+ int err;
+
+ *value = 0;
+ err = read_str_safe(fd, buf, sizeof(buf));
+ if (err < 0)
+ return err;
+ errno = 0;
+ v = strtoul(buf, &end, base);
+ if (end == buf || !isspace(*end))
+ return -EINVAL;
+ *value = v;
+ return 0;
+}
+
+/*
+ * Read one logical line from a configuration file.
+ *
+ * Line endings may be escaped with backslashes, to form one logical line from
+ * several physical lines. No end of line character(s) are included in the
+ * result.
+ *
+ * If linenum is not NULL, it is incremented by the number of physical lines
+ * which have been read.
+ */
+char *freadline_wrapped(FILE *fp, unsigned int *linenum)
+{
+ int size = 256;
+ int i = 0, n = 0;
+ _cleanup_free_ char *buf = malloc(size);
+
+ if (buf == NULL)
+ return NULL;
+
+ for(;;) {
+ int ch = getc_unlocked(fp);
+
+ switch(ch) {
+ case EOF:
+ if (i == 0)
+ return NULL;
+ /* else fall through */
+
+ case '\n':
+ n++;
+
+ {
+ char *ret = buf;
+ ret[i] = '\0';
+ buf = NULL;
+ if (linenum)
+ *linenum += n;
+ return ret;
+ }
+
+ case '\\':
+ ch = getc_unlocked(fp);
+
+ if (ch == '\n') {
+ n++;
+ continue;
+ }
+ /* else fall through */
+
+ default:
+ buf[i++] = ch;
+
+ if (i == size) {
+ char *tmp;
+ size *= 2;
+ tmp = realloc(buf, size);
+ if (!tmp)
+ return NULL;
+ buf = tmp;
+ }
+ }
+ }
+}
+
+/* path handling functions */
+/* ************************************************************************ */
+
+static bool path_is_absolute(const char *p)
+{
+ assert(p != NULL);
+
+ return p[0] == '/';
+}
+
+char *path_make_absolute_cwd(const char *p)
+{
+ _cleanup_free_ char *cwd = NULL;
+ size_t plen, cwdlen;
+ char *r;
+
+ if (path_is_absolute(p))
+ return strdup(p);
+
+ cwd = get_current_dir_name();
+ if (!cwd)
+ return NULL;
+
+ plen = strlen(p);
+ cwdlen = strlen(cwd);
+
+ /* cwd + '/' + p + '\0' */
+ r = realloc(cwd, cwdlen + 1 + plen + 1);
+ if (r == NULL)
+ return NULL;
+
+ cwd = NULL;
+ r[cwdlen] = '/';
+ memcpy(&r[cwdlen + 1], p, plen + 1);
+
+ return r;
+}
+
+static inline int is_dir(const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st) >= 0)
+ return S_ISDIR(st.st_mode);
+
+ return -errno;
+}
+
+int mkdir_p(const char *path, int len, mode_t mode)
+{
+ char *start, *end;
+
+ start = strndupa(path, len);
+ end = start + len;
+
+ /*
+ * scan backwards, replacing '/' with '\0' while the component doesn't
+ * exist
+ */
+ for (;;) {
+ int r = is_dir(start);
+ if (r > 0) {
+ end += strlen(end);
+
+ if (end == start + len)
+ return 0;
+
+ /* end != start, since it would be caught on the first
+ * iteration */
+ *end = '/';
+ break;
+ } else if (r == 0)
+ return -ENOTDIR;
+
+ if (end == start)
+ break;
+
+ *end = '\0';
+
+ /* Find the next component, backwards, discarding extra '/'*/
+ while (end > start && *end != '/')
+ end--;
+
+ while (end > start && *(end - 1) == '/')
+ end--;
+ }
+
+ for (; end < start + len;) {
+ if (mkdir(start, mode) < 0 && errno != EEXIST)
+ return -errno;
+
+ end += strlen(end);
+ *end = '/';
+ }
+
+ return 0;
+}
+
+int mkdir_parents(const char *path, mode_t mode)
+{
+ char *end = strrchr(path, '/');
+
+ /* no parent directories */
+ if (end == NULL)
+ return 0;
+
+ return mkdir_p(path, end - path, mode);
+}
+
+static unsigned long long ts_usec(const struct timespec *ts)
+{
+ return (unsigned long long) ts->tv_sec * USEC_PER_SEC +
+ (unsigned long long) ts->tv_nsec / NSEC_PER_USEC;
+}
+
+static unsigned long long ts_msec(const struct timespec *ts)
+{
+ return (unsigned long long) ts->tv_sec * MSEC_PER_SEC +
+ (unsigned long long) ts->tv_nsec / NSEC_PER_MSEC;
+}
+
+static struct timespec msec_ts(unsigned long long msec)
+{
+ struct timespec ts = {
+ .tv_sec = msec / MSEC_PER_SEC,
+ .tv_nsec = (msec % MSEC_PER_SEC) * NSEC_PER_MSEC,
+ };
+
+ return ts;
+}
+
+int sleep_until_msec(unsigned long long msec)
+{
+ struct timespec ts = msec_ts(msec);
+
+ if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) < 0 &&
+ errno != EINTR)
+ return -errno;
+
+ return 0;
+}
+
+/*
+ * Exponential retry backoff with tail
+ */
+unsigned long long get_backoff_delta_msec(unsigned long long t0,
+ unsigned long long tend,
+ unsigned long long *delta)
+{
+ unsigned long long t;
+
+ t = now_msec();
+
+ if (!*delta)
+ *delta = 1;
+ else
+ *delta <<= 1;
+
+ while (t + *delta > tend)
+ *delta >>= 1;
+
+ if (!*delta && tend > t)
+ *delta = tend - t;
+
+ return t + *delta;
+}
+
+unsigned long long now_usec(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ return 0;
+
+ return ts_usec(&ts);
+}
+
+unsigned long long now_msec(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ return 0;
+
+ return ts_msec(&ts);
+}
+
+unsigned long long stat_mstamp(const struct stat *st)
+{
+#ifdef HAVE_STRUCT_STAT_ST_MTIM
+ return ts_usec(&st->st_mtim);
+#else
+ return (unsigned long long) st->st_mtime;
+#endif
+}
diff --git a/shared/util.h b/shared/util.h
new file mode 100644
index 0000000..c4a3916
--- /dev/null
+++ b/shared/util.h
@@ -0,0 +1,104 @@
+#pragma once
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include <shared/macro.h>
+
+/* string handling functions and memory allocations */
+/* ************************************************************************ */
+#define streq(a, b) (strcmp((a), (b)) == 0)
+#define strstartswith(a, b) (strncmp(a, b, strlen(b)) == 0)
+char *strchr_replace(char *s, char c, char r);
+void *memdup(const void *p, size_t n) __attribute__((nonnull(1)));
+
+/* module-related functions */
+/* ************************************************************************ */
+#define KMOD_EXTENSION_UNCOMPRESSED ".ko"
+
+int alias_normalize(const char *alias, char buf[static PATH_MAX], size_t *len) _must_check_ __attribute__((nonnull(1,2)));
+int underscores(char *s) _must_check_;
+char *modname_normalize(const char *modname, char buf[static PATH_MAX], size_t *len) __attribute__((nonnull(1, 2)));
+char *path_to_modname(const char *path, char buf[static PATH_MAX], size_t *len) __attribute__((nonnull(2)));
+bool path_ends_with_kmod_ext(const char *path, size_t len) __attribute__((nonnull(1)));
+
+/* read-like and fread-like functions */
+/* ************************************************************************ */
+ssize_t read_str_safe(int fd, char *buf, size_t buflen) _must_check_ __attribute__((nonnull(2)));
+ssize_t write_str_safe(int fd, const char *buf, size_t buflen) __attribute__((nonnull(2)));
+int read_str_long(int fd, long *value, int base) _must_check_ __attribute__((nonnull(2)));
+int read_str_ulong(int fd, unsigned long *value, int base) _must_check_ __attribute__((nonnull(2)));
+char *freadline_wrapped(FILE *fp, unsigned int *linenum) __attribute__((nonnull(1)));
+
+/* path handling functions */
+/* ************************************************************************ */
+char *path_make_absolute_cwd(const char *p) _must_check_ __attribute__((nonnull(1)));
+int mkdir_p(const char *path, int len, mode_t mode);
+int mkdir_parents(const char *path, mode_t mode);
+unsigned long long stat_mstamp(const struct stat *st);
+
+/* time-related functions
+ * ************************************************************************ */
+#define USEC_PER_SEC 1000000ULL
+#define USEC_PER_MSEC 1000ULL
+#define MSEC_PER_SEC 1000ULL
+#define NSEC_PER_MSEC 1000000ULL
+
+unsigned long long now_usec(void);
+unsigned long long now_msec(void);
+int sleep_until_msec(unsigned long long msec);
+unsigned long long get_backoff_delta_msec(unsigned long long t0,
+ unsigned long long tend,
+ unsigned long long *delta);
+
+
+/* endianess and alignments */
+/* ************************************************************************ */
+#define get_unaligned(ptr) \
+({ \
+ struct __attribute__((packed)) { \
+ typeof(*(ptr)) __v; \
+ } *__p = (typeof(__p)) (ptr); \
+ __p->__v; \
+})
+
+#define put_unaligned(val, ptr) \
+do { \
+ struct __attribute__((packed)) { \
+ typeof(*(ptr)) __v; \
+ } *__p = (typeof(__p)) (ptr); \
+ __p->__v = (val); \
+} while(0)
+
+static _always_inline_ unsigned int ALIGN_POWER2(unsigned int u)
+{
+ return 1 << ((sizeof(u) * 8) - __builtin_clz(u - 1));
+}
+
+/* misc */
+/* ************************************************************************ */
+static inline void freep(void *p) {
+ free(*(void**) p);
+}
+#define _cleanup_free_ _cleanup_(freep)
+
+static inline bool addu64_overflow(uint64_t a, uint64_t b, uint64_t *res)
+{
+#if (HAVE___BUILTIN_UADDL_OVERFLOW && HAVE___BUILTIN_UADDLL_OVERFLOW)
+#if __SIZEOF_LONG__ == 8
+ return __builtin_uaddl_overflow(a, b, res);
+#elif __SIZEOF_LONG_LONG__ == 8
+ return __builtin_uaddll_overflow(a, b, res);
+#else
+#error "sizeof(long long) != 8"
+#endif
+#endif
+ *res = a + b;
+ return UINT64_MAX - a < b;
+}
diff --git a/shell-completion/bash/kmod b/shell-completion/bash/kmod
new file mode 100644
index 0000000..f530836
--- /dev/null
+++ b/shell-completion/bash/kmod
@@ -0,0 +1,103 @@
+# kmod completion -*- shell-script -*-
+#
+# This file is part of kmod.
+#
+# Copyright 2010 Ran Benita
+# Copyright (C) 2013 Intel Corporation. All rights reserved.
+#
+# 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+__contains_word () {
+ local word=$1; shift
+ for w in "$@"; do [[ "$w" = "$word" ]] && return 0; done
+ return 1
+}
+
+__is_opt () {
+ local prevprev=${COMP_WORDS[COMP_CWORD-2]}
+ local short="$1" long="$2"
+
+ if [[ "$prev" = "$short" || "$prev" = "$long" ]]; then
+ declare -g cur=${cur#=}
+ return 0
+ elif [[ "$prev" = "=" && "$prevprev" = "$long" ]]; then
+ return 0
+ fi
+
+ return 1
+}
+
+_kmod_static_nodes () {
+ local OPTS='-o -f -h --help'
+ local OPTS_EQUAL='--output --format'
+ local GROUP_FORMAT='human tmpfiles devname'
+
+ if __is_opt '-o' '--output'; then
+ compopt -o filenames
+ COMPREPLY=( $(compgen -f -- "$cur") )
+ return 0
+ elif __is_opt '-f' '--format'; then
+ COMPREPLY=( $(compgen -W "$GROUP_FORMAT" -- "$cur" ) )
+ return 0
+ fi
+
+ local cur=${COMP_WORDS[COMP_CWORD]}
+
+ compopt -o nospace
+ COMPREPLY=( $(compgen -W "$OPTS" -- "$cur") )
+ COMPREPLY+=( $(compgen -W "$OPTS_EQUAL" -S= -- "$cur") )
+}
+
+_kmod() {
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local VERBS=(help list static-nodes)
+ local OPTS='-h --help -V --version'
+ local verb
+
+ # standalone options, no other option or action allowed
+ for ((i=0; $i < $COMP_CWORD; i++)); do
+ if __contains_word "${COMP_WORDS[i]}" ${OPTS}; then
+ return 0
+ fi
+ done
+
+ # find the action
+ 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
+ COMPREPLY=( $(compgen -W '${OPTS[*]} ${VERBS[*]}' -- "$cur") )
+ return 0
+ fi
+
+ local func=${verb//-/_}
+
+ if declare -F _kmod_${func} > /dev/null; then
+ _kmod_${func}
+ fi
+
+ # allow the space if there's only one completion and it doesn't end with
+ # '='
+ if [[ ${#COMPREPLY[@]} == 1 && ${COMPREPLY[0]} != *"=" ]] ; then
+ compopt +o nospace
+ fi
+
+ return 0
+}
+
+complete -F _kmod kmod
diff --git a/testsuite/.gitignore b/testsuite/.gitignore
new file mode 100644
index 0000000..9d26b88
--- /dev/null
+++ b/testsuite/.gitignore
@@ -0,0 +1,57 @@
+*.lo
+*.la
+*.so
+/.dirstamp
+/test-scratchbuf
+/test-strbuf
+/test-array
+/test-util
+/test-blacklist
+/test-dependencies
+/test-depmod
+/test-init
+/test-initstate
+/test-loaded
+/test-modinfo
+/test-new-module
+/test-testsuite
+/test-modprobe
+/test-hash
+/test-list
+/test-tools
+/rootfs
+/stamp-rootfs
+/test-scratchbuf.log
+/test-scratchbuf.trs
+/test-strbuf.log
+/test-strbuf.trs
+/test-array.log
+/test-array.trs
+/test-util.log
+/test-util.trs
+/test-blacklist.log
+/test-blacklist.trs
+/test-dependencies.log
+/test-dependencies.trs
+/test-depmod.log
+/test-depmod.trs
+/test-init.log
+/test-init.trs
+/test-initstate.log
+/test-initstate.trs
+/test-loaded.log
+/test-loaded.trs
+/test-modinfo.log
+/test-modinfo.trs
+/test-modprobe.log
+/test-modprobe.trs
+/test-hash.log
+/test-hash.trs
+/test-new-module.log
+/test-new-module.trs
+/test-testsuite.log
+/test-testsuite.trs
+/test-list.log
+/test-list.trs
+/test-tools.log
+/test-tools.trs
diff --git a/testsuite/COPYING b/testsuite/COPYING
new file mode 100644
index 0000000..8add30a
--- /dev/null
+++ b/testsuite/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/testsuite/Makefile b/testsuite/Makefile
new file mode 100644
index 0000000..38ba552
--- /dev/null
+++ b/testsuite/Makefile
@@ -0,0 +1,15 @@
+# Copyright 2010 Lennart Poettering
+#
+# This file has been copied from systemd. It is a dirty trick to simplify
+# compilation when CWD is not the root of the source tree. This file is not
+# intended to be distributed. So, don't touch it, even better ignore it!
+
+all:
+ $(MAKE) -C .. check
+check:
+ $(MAKE) -C .. check
+
+clean:
+ $(MAKE) -C .. clean
+
+.PHONY: all clean check
diff --git a/testsuite/README b/testsuite/README
new file mode 100644
index 0000000..052569f
--- /dev/null
+++ b/testsuite/README
@@ -0,0 +1,61 @@
+testsuite
+
+OVERVIEW
+========
+
+Kmod's testsuite was designed to automate the process of running tests with
+different scenarios, configurations and architectures. The idea is that once we
+received a bug report, we reproduce it using the testsuite so we avoid
+recurring on the same bug in future.
+
+
+FEATURES
+========
+
+- Isolate each test by running them in separate processes;
+- Exec a binary, so we can test the tools and not only the lib API
+- Fake accesses to filesystem so we can provide a test rootfs with all the
+ configuration, indexes, etc that test needs to be executed.
+- Fake calls to init_module(), delete_module() and uname(), so we don't have to
+ run tests as root and figure out how to deal with different architectures.
+
+HOW TO ADD A TEST
+=================
+
+The simplest way to add a test is to copy and paste one already there. Most of
+the options you can have are covered by another tests. This is what you need to
+pay attention when writing a test:
+
+1 - Look at testsuite.h, struct test, to see all the options available.
+
+2 - Use TESTSUITE_MAIN and DEFINE_TEST to add new tests. Don't forget to fill
+ its description.
+
+3 - If you want testsuite to compare the stdout/stderr of your tests in order
+ to check if it worked or not, fill in output.{stderr,stdout} the file with
+ the expected output. Bear in mind the same file is used for all
+ architectures, so don't print arch-dependent content if you are comparing
+ the output.
+
+4 - Fill in the config vector. Setting any of these configuration will make
+ testsuite to export LD_PRELOAD with the necessary override libs before
+ executing the test. If you are not exec'ing an external binary, you need to
+ pass "need_spawn = true" below, otherwise it will not work (LD_PRELOAD is
+ only applied when exec'ing a binary). See each config description in
+ testsuite.h
+
+5 - need_spawn: if testsuite will exec itself before running a test
+
+6 - expected_fail: if that test is expected to fail, i.e. the return code is
+ expected not to be 0.
+
+7 - The rootfs is populated by copying the entire contents of rootfs-pristine
+ then running populate-modules.sh to copy generated modules from
+ module-playground. Update the latter script to include any modules your
+ test need.
+
+8 - Tests can be run individually, outside of 'make check'. strace and gdb work
+ too, as long as you tell them to operate on child process.
+
+9 - Make sure test passes when using "default" build flags, i.e. by running
+ 'autogen.sh c'
diff --git a/testsuite/delete_module.c b/testsuite/delete_module.c
new file mode 100644
index 0000000..f3ae20b
--- /dev/null
+++ b/testsuite/delete_module.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <shared/util.h>
+
+#include "testsuite.h"
+
+struct mod {
+ struct mod *next;
+ int ret;
+ int errcode;
+ char name[];
+};
+
+static struct mod *modules;
+static bool need_init = true;
+
+static void parse_retcodes(struct mod **_modules, const char *s)
+{
+ const char *p;
+
+ if (s == NULL)
+ return;
+
+ for (p = s;;) {
+ struct mod *mod;
+ const char *modname;
+ char *end;
+ size_t modnamelen;
+ int ret, errcode;
+ long l;
+
+ modname = p;
+ if (modname == NULL || modname[0] == '\0')
+ break;
+
+ modnamelen = strcspn(p, ":");
+ if (modname[modnamelen] != ':')
+ break;
+
+ p = modname + modnamelen + 1;
+ if (p == NULL)
+ break;
+
+ l = strtol(p, &end, 0);
+ if (end == p || *end != ':')
+ break;
+
+ ret = (int) l;
+ p = end + 1;
+
+ l = strtol(p, &end, 0);
+ if (*end == ':')
+ p = end + 1;
+ else if (*end != '\0')
+ break;
+
+ errcode = (int) l;
+
+ mod = malloc(sizeof(*mod) + modnamelen + 1);
+ if (mod == NULL)
+ break;
+
+ memcpy(mod->name, modname, modnamelen);
+ mod->name[modnamelen] = '\0';
+ mod->ret = ret;
+ mod->errcode = errcode;
+ mod->next = *_modules;
+ *_modules = mod;
+ }
+}
+
+static struct mod *find_module(struct mod *_modules, const char *modname)
+{
+ struct mod *mod;
+
+ for (mod = _modules; mod != NULL; mod = mod->next) {
+ if (streq(mod->name, modname))
+ return mod;
+ }
+
+ return NULL;
+}
+
+static void init_retcodes(void)
+{
+ const char *s;
+ struct mod *mod;
+
+ if (!need_init)
+ return;
+
+ need_init = false;
+ s = getenv(S_TC_DELETE_MODULE_RETCODES);
+ if (s == NULL) {
+ ERR("TRAP delete_module(): missing export %s?\n",
+ S_TC_DELETE_MODULE_RETCODES);
+ }
+
+ parse_retcodes(&modules, s);
+
+ for (mod = modules; mod != NULL; mod = mod->next) {
+ LOG("Added module to test delete_module:\n");
+ LOG("\tname=%s ret=%d errcode=%d\n",
+ mod->name, mod->ret, mod->errcode);
+ }
+}
+
+TS_EXPORT long delete_module(const char *name, unsigned int flags);
+
+/*
+ * FIXME: change /sys/module/<modname> to fake-remove a module
+ *
+ * Default behavior is to exit successfully. If this is not the intended
+ * behavior, set TESTSUITE_DELETE_MODULE_RETCODES env var.
+ */
+long delete_module(const char *modname, unsigned int flags)
+{
+ struct mod *mod;
+
+ init_retcodes();
+ mod = find_module(modules, modname);
+ if (mod == NULL)
+ return 0;
+
+ errno = mod->errcode;
+ return mod->ret;
+}
+
+/* the test is going away anyway, but lets keep valgrind happy */
+void free_resources(void) __attribute__((destructor));
+void free_resources(void)
+{
+ while (modules) {
+ struct mod *mod = modules->next;
+ free(modules);
+ modules = mod;
+ }
+}
diff --git a/testsuite/init_module.c b/testsuite/init_module.c
new file mode 100644
index 0000000..503e703
--- /dev/null
+++ b/testsuite/init_module.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ * Copyright (C) 2012-2013 Lucas De Marchi <lucas.de.marchi@gmail.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HAVE_FINIT_MODULE
+#define HAVE_FINIT_MODULE 1
+#endif
+
+#include <assert.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#include <shared/util.h>
+
+/* kmod_elf_get_section() is not exported, we need the private header */
+#include <libkmod/libkmod-internal.h>
+
+/* FIXME: hack, change name so we don't clash */
+#undef ERR
+#include "testsuite.h"
+#include "stripped-module.h"
+
+struct mod {
+ struct mod *next;
+ int ret;
+ int errcode;
+ char name[];
+};
+
+static struct mod *modules;
+static bool need_init = true;
+static struct kmod_ctx *ctx;
+
+static void parse_retcodes(struct mod *_modules, const char *s)
+{
+ const char *p;
+
+ if (s == NULL)
+ return;
+
+ for (p = s;;) {
+ struct mod *mod;
+ const char *modname;
+ char *end;
+ size_t modnamelen;
+ int ret, errcode;
+ long l;
+
+ modname = p;
+ if (modname == NULL || modname[0] == '\0')
+ break;
+
+ modnamelen = strcspn(s, ":");
+ if (modname[modnamelen] != ':')
+ break;
+
+ p = modname + modnamelen + 1;
+ if (p == NULL)
+ break;
+
+ l = strtol(p, &end, 0);
+ if (end == p || *end != ':')
+ break;
+ ret = (int) l;
+ p = end + 1;
+
+ l = strtol(p, &end, 0);
+ if (*end == ':')
+ p = end + 1;
+ else if (*end != '\0')
+ break;
+
+ errcode = (int) l;
+
+ mod = malloc(sizeof(*mod) + modnamelen + 1);
+ if (mod == NULL)
+ break;
+
+ memcpy(mod->name, modname, modnamelen);
+ mod->name[modnamelen] = '\0';
+ mod->ret = ret;
+ mod->errcode = errcode;
+ mod->next = _modules;
+ _modules = mod;
+ }
+}
+
+static int write_one_line_file(const char *fn, const char *line, int len)
+{
+ FILE *f;
+ int r;
+
+ assert(fn);
+ assert(line);
+
+ f = fopen(fn, "we");
+ if (!f)
+ return -errno;
+
+ errno = 0;
+ if (fputs(line, f) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ fflush(f);
+
+ if (ferror(f)) {
+ if (errno != 0)
+ r = -errno;
+ else
+ r = -EIO;
+ } else
+ r = 0;
+
+finish:
+ fclose(f);
+ return r;
+}
+
+static int create_sysfs_files(const char *modname)
+{
+ char buf[PATH_MAX];
+ const char *sysfsmod = "/sys/module/";
+ int len = strlen(sysfsmod);
+
+ memcpy(buf, sysfsmod, len);
+ strcpy(buf + len, modname);
+ len += strlen(modname);
+
+ assert(mkdir_p(buf, len, 0755) >= 0);
+
+ strcpy(buf + len, "/initstate");
+ return write_one_line_file(buf, "live\n", strlen("live\n"));
+}
+
+static struct mod *find_module(struct mod *_modules, const char *modname)
+{
+ struct mod *mod;
+
+ for (mod = _modules; mod != NULL; mod = mod->next) {
+ if (streq(mod->name, modname))
+ return mod;
+ }
+
+ return NULL;
+}
+
+static void init_retcodes(void)
+{
+ const char *s;
+
+ if (!need_init)
+ return;
+
+ need_init = false;
+ s = getenv(S_TC_INIT_MODULE_RETCODES);
+ if (s == NULL) {
+ fprintf(stderr, "TRAP init_module(): missing export %s?\n",
+ S_TC_INIT_MODULE_RETCODES);
+ }
+
+ ctx = kmod_new(NULL, NULL);
+
+ parse_retcodes(modules, s);
+}
+
+static inline bool module_is_inkernel(const char *modname)
+{
+ struct kmod_module *mod;
+ int state;
+ bool ret;
+
+ if (kmod_module_new_from_name(ctx, modname, &mod) < 0)
+ return false;
+
+ state = kmod_module_get_initstate(mod);
+
+ if (state == KMOD_MODULE_LIVE ||
+ state == KMOD_MODULE_BUILTIN)
+ ret = true;
+ else
+ ret = false;
+
+ kmod_module_unref(mod);
+
+ return ret;
+}
+
+static uint8_t elf_identify(void *mem)
+{
+ uint8_t *p = mem;
+ return p[EI_CLASS];
+}
+
+TS_EXPORT long init_module(void *mem, unsigned long len, const char *args);
+
+/*
+ * Default behavior is to try to mimic init_module behavior inside the kernel.
+ * If it is a simple test that you know the error code, set the return code
+ * in TESTSUITE_INIT_MODULE_RETCODES env var instead.
+ *
+ * The exception is when the module name is not find in the memory passed.
+ * This is because we want to be able to pass dummy modules (and not real
+ * ones) and it still work.
+ */
+long init_module(void *mem, unsigned long len, const char *args)
+{
+ const char *modname;
+ struct kmod_elf *elf;
+ struct mod *mod;
+ const void *buf;
+ uint64_t bufsize;
+ int err;
+ uint8_t class;
+ off_t offset;
+
+ init_retcodes();
+
+ elf = kmod_elf_new(mem, len);
+ if (elf == NULL)
+ return 0;
+
+ err = kmod_elf_get_section(elf, ".gnu.linkonce.this_module", &buf,
+ &bufsize);
+ kmod_elf_unref(elf);
+
+ /* We couldn't parse the ELF file. Just exit as if it was successful */
+ if (err < 0)
+ return 0;
+
+ /* We need to open both 32 and 64 bits module - hack! */
+ class = elf_identify(mem);
+ if (class == ELFCLASS64)
+ offset = MODULE_NAME_OFFSET_64;
+ else
+ offset = MODULE_NAME_OFFSET_32;
+
+ modname = (char *)buf + offset;
+ mod = find_module(modules, modname);
+ if (mod != NULL) {
+ errno = mod->errcode;
+ err = mod->ret;
+ } else if (module_is_inkernel(modname)) {
+ err = -1;
+ errno = EEXIST;
+ } else
+ err = 0;
+
+ if (err == 0)
+ create_sysfs_files(modname);
+
+ return err;
+}
+
+static int check_kernel_version(int major, int minor)
+{
+ struct utsname u;
+ const char *p;
+ int maj = 0, min = 0;
+
+ if (uname(&u) < 0)
+ return false;
+ for (p = u.release; *p >= '0' && *p <= '9'; p++)
+ maj = maj * 10 + *p - '0';
+ if (*p == '.')
+ for (p++; *p >= '0' && *p <= '9'; p++)
+ min = min * 10 + *p - '0';
+ if (maj > major || (maj == major && min >= minor))
+ return true;
+ return false;
+}
+
+
+TS_EXPORT int finit_module(const int fd, const char *args, const int flags);
+
+int finit_module(const int fd, const char *args, const int flags)
+{
+ int err;
+ void *mem;
+ unsigned long len;
+ struct stat st;
+
+ if (!check_kernel_version(3, 8)) {
+ errno = ENOSYS;
+ return -1;
+ }
+ if (fstat(fd, &st) < 0)
+ return -1;
+
+ len = st.st_size;
+ mem = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (mem == MAP_FAILED)
+ return -1;
+
+ err = init_module(mem, len, args);
+ munmap(mem, len);
+
+ return err;
+}
+
+TS_EXPORT long int syscall(long int __sysno, ...)
+{
+ va_list ap;
+ long ret;
+
+ if (__sysno == -1) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (__sysno == __NR_finit_module) {
+ const char *args;
+ int flags;
+ int fd;
+
+ va_start(ap, __sysno);
+
+ fd = va_arg(ap, int);
+ args = va_arg(ap, const char *);
+ flags = va_arg(ap, int);
+
+ ret = finit_module(fd, args, flags);
+
+ va_end(ap);
+ return ret;
+ }
+
+ if (__sysno == __NR_gettid) {
+ static void *nextlib = NULL;
+ static long (*nextlib_syscall)(long number, ...);
+
+ if (nextlib_syscall == NULL) {
+#ifdef RTLD_NEXT
+ nextlib = RTLD_NEXT;
+#else
+ nextlib = dlopen("libc.so.6", RTLD_LAZY);
+#endif
+ nextlib_syscall = dlsym(nextlib, "syscall");
+ if (nextlib_syscall == NULL) {
+ fprintf(stderr, "FIXME FIXME FIXME: could not load syscall symbol: %s\n",
+ dlerror());
+ abort();
+ }
+ }
+
+ return nextlib_syscall(__NR_gettid);
+ }
+
+ /*
+ * FIXME: no way to call the libc function due since this is a
+ * variadic argument function and we don't have a vsyscall() variant
+ * this may fail if a library or process is trying to call syscall()
+ * directly, for example to implement gettid().
+ */
+ fprintf(stderr, "FIXME FIXME FIXME: could not wrap call to syscall(%ld), this should not happen\n",
+ __sysno);
+
+ abort();
+}
+
+/* the test is going away anyway, but lets keep valgrind happy */
+void free_resources(void) __attribute__((destructor));
+void free_resources(void)
+{
+ while (modules) {
+ struct mod *mod = modules->next;
+ free(modules);
+ modules = mod;
+ }
+
+ if (ctx)
+ kmod_unref(ctx);
+}
diff --git a/testsuite/module-playground/.gitignore b/testsuite/module-playground/.gitignore
new file mode 100644
index 0000000..6d9c7b1
--- /dev/null
+++ b/testsuite/module-playground/.gitignore
@@ -0,0 +1,16 @@
+*.ko
+!mod-simple-*.ko
+!cache/*.ko
+.cache.mk
+*.mod.c
+.tmp_versions
+*.mod
+*.a
+*.cmd
+*.o.d
+
+modules.order
+Module.symvers
+mod-simple-x86_64.c
+mod-simple-i386.c
+mod-simple-sparc64.c
diff --git a/testsuite/module-playground/Makefile b/testsuite/module-playground/Makefile
new file mode 100644
index 0000000..a7ab09b
--- /dev/null
+++ b/testsuite/module-playground/Makefile
@@ -0,0 +1,83 @@
+ifneq ($(KERNELRELEASE),)
+# kbuild part of makefile
+
+ifneq ($(KMOD_TESTSUITE_ARCH_BUILD),1)
+obj-m := mod-simple.o
+
+# mod-foo depends on foo-x, and foo-x modules don't depend
+# on anyone
+obj-m += mod-foo-a.o
+obj-m += mod-foo-b.o
+obj-m += mod-foo-c.o
+obj-m += mod-foo.o
+
+# mod-loop: create loops in dependencies:
+# 1) mod-loop-a -> mod-loop-b -> mod-loop-c -> mod-loop-a
+# |-> mod-loop-f |-> mod-loop-f
+# \-> mod-loop-g \-> mod-loop-g
+# 2) mod-loop-d -> mod-loop-e -> mod-loop-d
+# 3.1) mod-loop-h -> mod-loop-i -> mod-loop-j -> mod-loop-h
+# 3.2) mod-loop-h -> mod-loop-i -> mod-loop-j -> mod-loop-k -> mod-loop-h
+
+obj-m += mod-loop-a.o
+obj-m += mod-loop-b.o
+obj-m += mod-loop-c.o
+obj-m += mod-loop-d.o
+obj-m += mod-loop-e.o
+obj-m += mod-loop-f.o
+obj-m += mod-loop-g.o
+obj-m += mod-loop-h.o
+obj-m += mod-loop-i.o
+obj-m += mod-loop-j.o
+obj-m += mod-loop-k.o
+
+# mod-fake-*: fake the respective modules in kernel with these aliases. Aliases
+# list was taken from 3.5.4
+obj-m += mod-fake-hpsa.o
+obj-m += mod-fake-scsi-mod.o
+obj-m += mod-fake-cciss.o
+
+else
+# only build ARCH-specific module
+ifeq ($(ARCH),)
+ $(error ARCH must be set to a valid architecture)
+endif
+obj-m := mod-simple-$(ARCH).o
+endif
+
+else
+# normal makefile
+KDIR ?= $(module_prefix)/lib/modules/`uname -r`/build
+KVER ?= `uname -r`
+ifeq ($(FAKE_BUILD),)
+ FAKE_BUILD=0
+endif
+
+ARCH_SPECIFIC_MODULES := mod-simple-x86_64.ko mod-simple-i386.ko mod-simple-sparc64.ko
+MY_MODULES := $(filter-out $(ARCH_SPECIFIC_MODULES),$(wildcard *.ko))
+
+default: modules arch-modules
+
+mod-simple-%.ko: mod-simple-%.c Makefile.arch
+ $(eval arch=$(patsubst mod-simple-%.ko,%,$@))
+ $(MAKE) KDIR=$(KDIR_$(arch)) ARCH=$(arch) CROSS_COMPILE=$(CROSS_COMPILE_$(arch)) -f Makefile.arch
+
+ifeq ($(FAKE_BUILD),0)
+modules:
+ $(MAKE) -C $(KDIR) M=$$PWD
+else
+modules:
+ @echo " CP cache/*.ko"
+ @cp cache/*.ko .
+endif
+
+arch-modules: $(ARCH_SPECIFIC_MODULES)
+
+clean:
+ test -z "$(MY_MODULES)" || rm -rf $(MY_MODULES) || true
+ rm -rf .tmp_versions || true
+ rm -rf .*.cmd *.mod.c || true
+ rm -rf *.o || true
+ rm -rf Module.symvers modules.order
+
+endif
diff --git a/testsuite/module-playground/Makefile.arch b/testsuite/module-playground/Makefile.arch
new file mode 100644
index 0000000..47bd9cb
--- /dev/null
+++ b/testsuite/module-playground/Makefile.arch
@@ -0,0 +1,14 @@
+ifeq ($(ARCH),)
+ $(error ARCH must be set to a valid architecture)
+endif
+
+default:
+ @cmp --quiet mod-simple.c mod-simple-$(ARCH).c || ( \
+ ln -sf mod-simple.c mod-simple-$(ARCH).c; \
+ /bin/false \
+ )
+ $(MAKE) -C $(KDIR_$(ARCH)) CROSS_COMPILE=$(CROSS_COMPILE_$(ARCH)) M=$$PWD KMOD_TESTSUITE_ARCH_BUILD=1
+
+clean:
+ $(MAKE) -C $(KDIR_$(ARCH)) CROSS_COMPILE=$(CROSS_COMPILE_$(ARCH)) M=$$PWD KMOD_TESTSUITE_ARCH_BUILD=1 clean
+ rm -f mod-simple-$(ARCH).c
diff --git a/testsuite/module-playground/README b/testsuite/module-playground/README
new file mode 100644
index 0000000..76c6e8d
--- /dev/null
+++ b/testsuite/module-playground/README
@@ -0,0 +1,26 @@
+Pre-compiled modules
+====================
+
+Some modules are pre-compiled due to needing cross-compilers present on the
+build/dev machine which is inconvenient. Makefile is ready to compile them again
+in case they are missing:
+
+1) Prepare the linux kernel trees to build external modules, i.e.:
+
+ kernel $ make ARCH=<arch> CROSS_COMPILER=<cross-compiler-prefix> defconfig
+ kernel $ make ARCH=<arch> CROSS_COMPILER=<cross-compiler-prefix> modules_prepare
+
+ For each architecture. See the Makefile to check which are the supported architectures.
+
+2) Export the variables below to point to the right place:
+
+ KDIR_<arch>: for each architecture it needs to point to a
+ kernel tree configured as in (1)
+
+ CROSS_COMPILER_<arch: for each architecture it needs to point to the
+ correct toolchain prefix. Leave it blank if a
+ cross-compiler is not needed (example: you are
+ building a 32b module with a multilib compiler).
+
+
+3) Remove every %-<arch>.ko. After this the build system will recreate them.
diff --git a/testsuite/module-playground/cache/mod-fake-cciss.ko b/testsuite/module-playground/cache/mod-fake-cciss.ko
new file mode 100644
index 0000000..f61409a
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-fake-cciss.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-fake-hpsa.ko b/testsuite/module-playground/cache/mod-fake-hpsa.ko
new file mode 100644
index 0000000..3d33ba7
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-fake-hpsa.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-fake-scsi-mod.ko b/testsuite/module-playground/cache/mod-fake-scsi-mod.ko
new file mode 100644
index 0000000..c0eb974
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-fake-scsi-mod.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-foo-a.ko b/testsuite/module-playground/cache/mod-foo-a.ko
new file mode 100644
index 0000000..f2d406e
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-foo-a.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-foo-b.ko b/testsuite/module-playground/cache/mod-foo-b.ko
new file mode 100644
index 0000000..53bf3a9
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-foo-b.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-foo-c.ko b/testsuite/module-playground/cache/mod-foo-c.ko
new file mode 100644
index 0000000..083e801
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-foo-c.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-foo.ko b/testsuite/module-playground/cache/mod-foo.ko
new file mode 100644
index 0000000..0961478
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-foo.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-a.ko b/testsuite/module-playground/cache/mod-loop-a.ko
new file mode 100644
index 0000000..51533d8
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-a.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-b.ko b/testsuite/module-playground/cache/mod-loop-b.ko
new file mode 100644
index 0000000..5e20348
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-b.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-c.ko b/testsuite/module-playground/cache/mod-loop-c.ko
new file mode 100644
index 0000000..6a02b04
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-c.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-d.ko b/testsuite/module-playground/cache/mod-loop-d.ko
new file mode 100644
index 0000000..3ab3348
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-d.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-e.ko b/testsuite/module-playground/cache/mod-loop-e.ko
new file mode 100644
index 0000000..efe93ff
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-e.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-f.ko b/testsuite/module-playground/cache/mod-loop-f.ko
new file mode 100644
index 0000000..c0e15cb
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-f.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-g.ko b/testsuite/module-playground/cache/mod-loop-g.ko
new file mode 100644
index 0000000..46f3c43
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-g.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-h.ko b/testsuite/module-playground/cache/mod-loop-h.ko
new file mode 100644
index 0000000..cbd81e5
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-h.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-i.ko b/testsuite/module-playground/cache/mod-loop-i.ko
new file mode 100644
index 0000000..045bc4f
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-i.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-j.ko b/testsuite/module-playground/cache/mod-loop-j.ko
new file mode 100644
index 0000000..067aa4b
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-j.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-loop-k.ko b/testsuite/module-playground/cache/mod-loop-k.ko
new file mode 100644
index 0000000..2cc16d1
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-loop-k.ko
Binary files differ
diff --git a/testsuite/module-playground/cache/mod-simple.ko b/testsuite/module-playground/cache/mod-simple.ko
new file mode 100644
index 0000000..2c5ffcf
--- /dev/null
+++ b/testsuite/module-playground/cache/mod-simple.ko
Binary files differ
diff --git a/testsuite/module-playground/dummy.pkcs7 b/testsuite/module-playground/dummy.pkcs7
new file mode 100644
index 0000000..bcdb902
--- /dev/null
+++ b/testsuite/module-playground/dummy.pkcs7
Binary files differ
diff --git a/testsuite/module-playground/dummy.sha1 b/testsuite/module-playground/dummy.sha1
new file mode 100644
index 0000000..bab65d3
--- /dev/null
+++ b/testsuite/module-playground/dummy.sha1
Binary files differ
diff --git a/testsuite/module-playground/dummy.sha256 b/testsuite/module-playground/dummy.sha256
new file mode 100644
index 0000000..fe16b01
--- /dev/null
+++ b/testsuite/module-playground/dummy.sha256
Binary files differ
diff --git a/testsuite/module-playground/mod-fake-cciss.c b/testsuite/module-playground/mod-fake-cciss.c
new file mode 100644
index 0000000..9c46801
--- /dev/null
+++ b/testsuite/module-playground/mod-fake-cciss.c
@@ -0,0 +1,37 @@
+#include <linux/init.h>
+#include <linux/module.h>
+
+static int __init test_module_init(void)
+{
+ return 0;
+}
+
+static void test_module_exit(void)
+{
+}
+module_init(test_module_init);
+module_exit(test_module_exit);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
+
+MODULE_ALIAS("pci:v00000E11d0000B060sv00000E11sd00004070bc*sc*i*");
+MODULE_ALIAS("pci:v00000E11d0000B178sv00000E11sd00004080bc*sc*i*");
+MODULE_ALIAS("pci:v00000E11d0000B178sv00000E11sd00004082bc*sc*i*");
+MODULE_ALIAS("pci:v00000E11d0000B178sv00000E11sd00004083bc*sc*i*");
+MODULE_ALIAS("pci:v00000E11d00000046sv00000E11sd00004091bc*sc*i*");
+MODULE_ALIAS("pci:v00000E11d00000046sv00000E11sd0000409Abc*sc*i*");
+MODULE_ALIAS("pci:v00000E11d00000046sv00000E11sd0000409Bbc*sc*i*");
+MODULE_ALIAS("pci:v00000E11d00000046sv00000E11sd0000409Cbc*sc*i*");
+MODULE_ALIAS("pci:v00000E11d00000046sv00000E11sd0000409Dbc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003220sv0000103Csd00003225bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003230sv0000103Csd00003223bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003230sv0000103Csd00003234bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003230sv0000103Csd00003235bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003238sv0000103Csd00003211bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003238sv0000103Csd00003212bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003238sv0000103Csd00003213bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003238sv0000103Csd00003214bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003238sv0000103Csd00003215bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003230sv0000103Csd00003237bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd00003230sv0000103Csd0000323Dbc*sc*i*");
diff --git a/testsuite/module-playground/mod-fake-hpsa.c b/testsuite/module-playground/mod-fake-hpsa.c
new file mode 100644
index 0000000..99dc729
--- /dev/null
+++ b/testsuite/module-playground/mod-fake-hpsa.c
@@ -0,0 +1,36 @@
+#include <linux/init.h>
+#include <linux/module.h>
+
+extern void dummy_export(void);
+
+static int __init test_module_init(void)
+{
+ dummy_export();
+ return 0;
+}
+
+static void test_module_exit(void)
+{
+}
+module_init(test_module_init);
+module_exit(test_module_exit);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
+
+MODULE_ALIAS("pci:v0000103Cd0000323Asv0000103Csd00003241bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Asv0000103Csd00003243bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Asv0000103Csd00003245bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Asv0000103Csd00003247bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Asv0000103Csd00003249bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Asv0000103Csd0000324Abc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Asv0000103Csd0000324Bbc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Asv0000103Csd00003233bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Bsv0000103Csd00003350bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Bsv0000103Csd00003351bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Bsv0000103Csd00003352bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Bsv0000103Csd00003353bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Bsv0000103Csd00003354bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Bsv0000103Csd00003355bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd0000323Bsv0000103Csd00003356bc*sc*i*");
+MODULE_ALIAS("pci:v0000103Cd*sv*sd*bc01sc04i*");
diff --git a/testsuite/module-playground/mod-fake-scsi-mod.c b/testsuite/module-playground/mod-fake-scsi-mod.c
new file mode 100644
index 0000000..916a04d
--- /dev/null
+++ b/testsuite/module-playground/mod-fake-scsi-mod.c
@@ -0,0 +1,21 @@
+#include <linux/init.h>
+#include <linux/module.h>
+
+static int __init test_module_init(void)
+{
+ return 0;
+}
+
+static void test_module_exit(void)
+{
+}
+module_init(test_module_init);
+module_exit(test_module_exit);
+
+void dummy_export(void)
+{
+}
+EXPORT_SYMBOL(dummy_export);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-foo-a.c b/testsuite/module-playground/mod-foo-a.c
new file mode 100644
index 0000000..bc65f66
--- /dev/null
+++ b/testsuite/module-playground/mod-foo-a.c
@@ -0,0 +1,20 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+static int __init foo_init(void)
+{
+ return 0;
+}
+module_init(foo_init);
+
+void print_fooA(void)
+{
+ pr_warn("fooA\n");
+}
+EXPORT_SYMBOL(print_fooA);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-foo-b.c b/testsuite/module-playground/mod-foo-b.c
new file mode 100644
index 0000000..09079f6
--- /dev/null
+++ b/testsuite/module-playground/mod-foo-b.c
@@ -0,0 +1,20 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+static int __init foo_init(void)
+{
+ return 0;
+}
+module_init(foo_init);
+
+void print_fooB(void)
+{
+ pr_warn("fooB\n");
+}
+EXPORT_SYMBOL(print_fooB);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-foo-c.c b/testsuite/module-playground/mod-foo-c.c
new file mode 100644
index 0000000..3afd35d
--- /dev/null
+++ b/testsuite/module-playground/mod-foo-c.c
@@ -0,0 +1,20 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+static int __init foo_init(void)
+{
+ return 0;
+}
+module_init(foo_init);
+
+void print_fooC(void)
+{
+ pr_warn("fooC\n");
+}
+EXPORT_SYMBOL(print_fooC);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-foo.c b/testsuite/module-playground/mod-foo.c
new file mode 100644
index 0000000..8105608
--- /dev/null
+++ b/testsuite/module-playground/mod-foo.c
@@ -0,0 +1,23 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+void print_fooA(void);
+void print_fooB(void);
+void print_fooC(void);
+
+static int __init foo_init(void)
+{
+ print_fooA();
+ print_fooB();
+ print_fooC();
+
+ return 0;
+}
+
+module_init(foo_init);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-a.c b/testsuite/module-playground/mod-loop-a.c
new file mode 100644
index 0000000..e5adb49
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-a.c
@@ -0,0 +1,27 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printA();
+ printB();
+ printF();
+ printG();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printA(void)
+{
+ pr_warn("Hello, world A\n");
+}
+EXPORT_SYMBOL(printA);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-b.c b/testsuite/module-playground/mod-loop-b.c
new file mode 100644
index 0000000..26232ea
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-b.c
@@ -0,0 +1,27 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printB();
+ printC();
+ printF();
+ printG();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printB(void)
+{
+ pr_warn("Hello, world B\n");
+}
+EXPORT_SYMBOL(printB);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-c.c b/testsuite/module-playground/mod-loop-c.c
new file mode 100644
index 0000000..0b90f7f
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-c.c
@@ -0,0 +1,25 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printC();
+ printA();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printC(void)
+{
+ pr_warn("Hello, world C\n");
+}
+EXPORT_SYMBOL(printC);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-d.c b/testsuite/module-playground/mod-loop-d.c
new file mode 100644
index 0000000..9b52305
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-d.c
@@ -0,0 +1,25 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printD();
+ printE();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printD(void)
+{
+ pr_warn("Hello, world D\n");
+}
+EXPORT_SYMBOL(printD);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-e.c b/testsuite/module-playground/mod-loop-e.c
new file mode 100644
index 0000000..a65acb2
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-e.c
@@ -0,0 +1,25 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printE();
+ printD();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printE(void)
+{
+ pr_warn("Hello, world E\n");
+}
+EXPORT_SYMBOL(printE);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-f.c b/testsuite/module-playground/mod-loop-f.c
new file mode 100644
index 0000000..0abb161
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-f.c
@@ -0,0 +1,24 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printF();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printF(void)
+{
+ pr_warn("Hello, world F\n");
+}
+EXPORT_SYMBOL(printF);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-g.c b/testsuite/module-playground/mod-loop-g.c
new file mode 100644
index 0000000..0965d76
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-g.c
@@ -0,0 +1,24 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printG();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printG(void)
+{
+ pr_warn("Hello, world G\n");
+}
+EXPORT_SYMBOL(printG);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-h.c b/testsuite/module-playground/mod-loop-h.c
new file mode 100644
index 0000000..6f457e2
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-h.c
@@ -0,0 +1,25 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printH();
+ printI();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printH(void)
+{
+ pr_warn("Hello, world H\n");
+}
+EXPORT_SYMBOL(printH);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-i.c b/testsuite/module-playground/mod-loop-i.c
new file mode 100644
index 0000000..6456217
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-i.c
@@ -0,0 +1,25 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printI();
+ printJ();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printI(void)
+{
+ pr_warn("Hello, world I\n");
+}
+EXPORT_SYMBOL(printI);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-j.c b/testsuite/module-playground/mod-loop-j.c
new file mode 100644
index 0000000..facc81f
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-j.c
@@ -0,0 +1,26 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printJ();
+ printH();
+ printK();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printJ(void)
+{
+ pr_warn("Hello, world J\n");
+}
+EXPORT_SYMBOL(printJ);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop-k.c b/testsuite/module-playground/mod-loop-k.c
new file mode 100644
index 0000000..49386cc
--- /dev/null
+++ b/testsuite/module-playground/mod-loop-k.c
@@ -0,0 +1,25 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+
+#include "mod-loop.h"
+
+static int __init test_module_init(void)
+{
+ printK();
+ printH();
+
+ return 0;
+}
+module_init(test_module_init);
+
+void printK(void)
+{
+ pr_warn("Hello, world K\n");
+}
+EXPORT_SYMBOL(printK);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("LGPL");
diff --git a/testsuite/module-playground/mod-loop.h b/testsuite/module-playground/mod-loop.h
new file mode 100644
index 0000000..b9f5e82
--- /dev/null
+++ b/testsuite/module-playground/mod-loop.h
@@ -0,0 +1,13 @@
+#pragma once
+
+void printA(void);
+void printB(void);
+void printC(void);
+void printD(void);
+void printE(void);
+void printF(void);
+void printG(void);
+void printH(void);
+void printI(void);
+void printJ(void);
+void printK(void);
diff --git a/testsuite/module-playground/mod-simple-i386.ko b/testsuite/module-playground/mod-simple-i386.ko
new file mode 100644
index 0000000..9bce6e0
--- /dev/null
+++ b/testsuite/module-playground/mod-simple-i386.ko
Binary files differ
diff --git a/testsuite/module-playground/mod-simple-sparc64.ko b/testsuite/module-playground/mod-simple-sparc64.ko
new file mode 100644
index 0000000..17242c8
--- /dev/null
+++ b/testsuite/module-playground/mod-simple-sparc64.ko
Binary files differ
diff --git a/testsuite/module-playground/mod-simple-x86_64.ko b/testsuite/module-playground/mod-simple-x86_64.ko
new file mode 100644
index 0000000..0159b99
--- /dev/null
+++ b/testsuite/module-playground/mod-simple-x86_64.ko
Binary files differ
diff --git a/testsuite/module-playground/mod-simple.c b/testsuite/module-playground/mod-simple.c
new file mode 100644
index 0000000..503e4d8
--- /dev/null
+++ b/testsuite/module-playground/mod-simple.c
@@ -0,0 +1,32 @@
+#include <linux/debugfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+static struct dentry *debugfs_dir;
+
+static int test_show(struct seq_file *s, void *data)
+{
+ seq_puts(s, "test");
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(test);
+
+static int __init test_module_init(void)
+{
+ debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ debugfs_create_file("test", 0444, debugfs_dir, NULL, &test_fops);
+
+ return 0;
+}
+
+static void test_module_exit(void)
+{
+ debugfs_remove_recursive(debugfs_dir);
+}
+
+module_init(test_module_init);
+module_exit(test_module_exit);
+
+MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/testsuite/path.c b/testsuite/path.c
new file mode 100644
index 0000000..5a291b1
--- /dev/null
+++ b/testsuite/path.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* We unset _FILE_OFFSET_BITS here so we can override both stat and stat64 on
+ * 32-bit architectures and forward each to the right libc function */
+#undef _FILE_OFFSET_BITS
+
+#include <assert.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <shared/util.h>
+
+#include "testsuite.h"
+
+static void *nextlib;
+static const char *rootpath;
+static size_t rootpathlen;
+
+static inline bool need_trap(const char *path)
+{
+ return path != NULL && path[0] == '/'
+ && !strstartswith(path, ABS_TOP_BUILDDIR);
+}
+
+static const char *trap_path(const char *path, char buf[PATH_MAX * 2])
+{
+ size_t len;
+
+ if (!need_trap(path))
+ return path;
+
+ len = strlen(path);
+
+ if (len + rootpathlen > PATH_MAX * 2) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ memcpy(buf, rootpath, rootpathlen);
+ strcpy(buf + rootpathlen, path);
+ return buf;
+}
+
+static bool get_rootpath(const char *f)
+{
+ if (rootpath != NULL)
+ return true;
+
+ rootpath = getenv(S_TC_ROOTFS);
+ if (rootpath == NULL) {
+ ERR("TRAP %s(): missing export %s?\n", f, S_TC_ROOTFS);
+ errno = ENOENT;
+ return false;
+ }
+
+ rootpathlen = strlen(rootpath);
+
+ return true;
+}
+
+static void *get_libc_func(const char *f)
+{
+ void *fp;
+
+ if (nextlib == NULL) {
+#ifdef RTLD_NEXT
+ nextlib = RTLD_NEXT;
+#else
+ nextlib = dlopen("libc.so.6", RTLD_LAZY);
+#endif
+ }
+
+ fp = dlsym(nextlib, f);
+ assert(fp);
+
+ return fp;
+}
+
+/* wrapper template for a function with one "const char* path" argument */
+#define WRAP_1ARG(rettype, failret, name) \
+TS_EXPORT rettype name(const char *path) \
+{ \
+ const char *p; \
+ char buf[PATH_MAX * 2]; \
+ static rettype (*_fn)(const char*); \
+ \
+ if (!get_rootpath(__func__)) \
+ return failret; \
+ _fn = get_libc_func(#name); \
+ p = trap_path(path, buf); \
+ if (p == NULL) \
+ return failret; \
+ return (*_fn)(p); \
+}
+
+/* wrapper template for a function with "const char* path" and another argument */
+#define WRAP_2ARGS(rettype, failret, name, arg2t) \
+TS_EXPORT rettype name(const char *path, arg2t arg2) \
+{ \
+ const char *p; \
+ char buf[PATH_MAX * 2]; \
+ static rettype (*_fn)(const char*, arg2t arg2); \
+ \
+ if (!get_rootpath(__func__)) \
+ return failret; \
+ _fn = get_libc_func(#name); \
+ p = trap_path(path, buf); \
+ if (p == NULL) \
+ return failret; \
+ return (*_fn)(p, arg2); \
+}
+
+/* wrapper template for open family */
+#define WRAP_OPEN(suffix) \
+TS_EXPORT int open ## suffix (const char *path, int flags, ...) \
+{ \
+ const char *p; \
+ char buf[PATH_MAX * 2]; \
+ static int (*_fn)(const char *path, int flags, ...); \
+ \
+ if (!get_rootpath(__func__)) \
+ return -1; \
+ _fn = get_libc_func("open" #suffix); \
+ p = trap_path(path, buf); \
+ if (p == NULL) \
+ return -1; \
+ \
+ if (flags & O_CREAT) { \
+ mode_t mode; \
+ va_list ap; \
+ \
+ va_start(ap, flags); \
+ mode = va_arg(ap, mode_t); \
+ va_end(ap); \
+ return _fn(p, flags, mode); \
+ } \
+ \
+ return _fn(p, flags); \
+}
+
+/*
+ * wrapper template for __xstat family
+ * This family got deprecated/dropped in glibc 2.32.9000, but we still need
+ * to keep it for a while for programs that were built against previous versions
+ */
+#define WRAP_VERSTAT(prefix, suffix) \
+TS_EXPORT int prefix ## stat ## suffix (int ver, \
+ const char *path, \
+ struct stat ## suffix *st); \
+TS_EXPORT int prefix ## stat ## suffix (int ver, \
+ const char *path, \
+ struct stat ## suffix *st) \
+{ \
+ const char *p; \
+ char buf[PATH_MAX * 2]; \
+ static int (*_fn)(int ver, const char *path, \
+ struct stat ## suffix *); \
+ _fn = get_libc_func(#prefix "stat" #suffix); \
+ \
+ if (!get_rootpath(__func__)) \
+ return -1; \
+ p = trap_path(path, buf); \
+ if (p == NULL) \
+ return -1; \
+ \
+ return _fn(ver, p, st); \
+}
+
+WRAP_1ARG(DIR*, NULL, opendir);
+WRAP_1ARG(int, -1, chdir);
+
+WRAP_2ARGS(FILE*, NULL, fopen, const char*);
+WRAP_2ARGS(FILE*, NULL, fopen64, const char*);
+WRAP_2ARGS(int, -1, mkdir, mode_t);
+WRAP_2ARGS(int, -1, access, int);
+WRAP_2ARGS(int, -1, stat, struct stat*);
+WRAP_2ARGS(int, -1, lstat, struct stat*);
+WRAP_2ARGS(int, -1, stat64, struct stat64*);
+WRAP_2ARGS(int, -1, lstat64, struct stat64*);
+WRAP_OPEN(64);
+
+WRAP_OPEN();
+
+#ifdef HAVE___XSTAT
+WRAP_VERSTAT(__x,);
+WRAP_VERSTAT(__lx,);
+WRAP_VERSTAT(__x,64);
+WRAP_VERSTAT(__lx,64);
+#endif
diff --git a/testsuite/rootfs-pristine/test-blacklist/etc/modprobe.d/modprobe.conf b/testsuite/rootfs-pristine/test-blacklist/etc/modprobe.d/modprobe.conf
new file mode 100644
index 0000000..126612f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-blacklist/etc/modprobe.d/modprobe.conf
@@ -0,0 +1,2 @@
+blacklist floppy
+blacklist pcspkr
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias.bin b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin.bin b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep
new file mode 100644
index 0000000..d9ebfa3
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep
@@ -0,0 +1,4 @@
+kernel/fs/foo/mod-foo-b.ko:
+kernel/mod-foo-c.ko:
+kernel/lib/mod-foo-a.ko:
+kernel/fs/mod-foo.ko: kernel/fs/foo/mod-foo-b.ko kernel/lib/mod-foo-a.ko kernel/mod-foo-c.ko
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep.bin b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep.bin
new file mode 100644
index 0000000..8874801
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.devname b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.order b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.order
new file mode 100644
index 0000000..85f2778
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.order
@@ -0,0 +1,7 @@
+kernel/fs/mbcache.ko
+kernel/fs/ext3/ext3.ko
+kernel/fs/ext2/ext2.ko
+kernel/fs/ext4/ext4.ko
+kernel/fs/jbd/jbd.ko
+kernel/fs/jbd2/jbd2.ko
+kernel/lib/crc16.ko
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.softdep b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols
new file mode 100644
index 0000000..ddb6ab6
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols
@@ -0,0 +1,4 @@
+# Aliases for symbols, used by symbol_request().
+alias symbol:print_fooA mod_foo_a
+alias symbol:print_fooC mod_foo_c
+alias symbol:print_fooB mod_foo_b
diff --git a/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols.bin b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols.bin
new file mode 100644
index 0000000..1add6e5
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-depmod/detect-loop/correct.txt b/testsuite/rootfs-pristine/test-depmod/detect-loop/correct.txt
new file mode 100644
index 0000000..ff28545
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/detect-loop/correct.txt
@@ -0,0 +1,5 @@
+depmod: ERROR: Cycle detected: mod_loop_d -> mod_loop_e -> mod_loop_d
+depmod: ERROR: Cycle detected: mod_loop_i -> mod_loop_j -> mod_loop_k -> mod_loop_h -> mod_loop_i
+depmod: ERROR: Cycle detected: mod_loop_i -> mod_loop_j -> mod_loop_h -> mod_loop_i
+depmod: ERROR: Cycle detected: mod_loop_c -> mod_loop_a -> mod_loop_b -> mod_loop_c
+depmod: ERROR: Found 9 modules in dependency cycles!
diff --git a/testsuite/rootfs-pristine/test-depmod/detect-loop/lib/modules/4.4.4/modules.order b/testsuite/rootfs-pristine/test-depmod/detect-loop/lib/modules/4.4.4/modules.order
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/detect-loop/lib/modules/4.4.4/modules.order
diff --git a/testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/correct-modules.alias b/testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/correct-modules.alias
new file mode 100644
index 0000000..5675329
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/correct-modules.alias
@@ -0,0 +1,37 @@
+# Aliases extracted from modules themselves.
+alias pci:v0000103Cd00003230sv0000103Csd0000323Dbc*sc*i* cciss
+alias pci:v0000103Cd00003230sv0000103Csd00003237bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003215bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003214bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003213bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003212bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003211bc*sc*i* cciss
+alias pci:v0000103Cd00003230sv0000103Csd00003235bc*sc*i* cciss
+alias pci:v0000103Cd00003230sv0000103Csd00003234bc*sc*i* cciss
+alias pci:v0000103Cd00003230sv0000103Csd00003223bc*sc*i* cciss
+alias pci:v0000103Cd00003220sv0000103Csd00003225bc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd0000409Dbc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd0000409Cbc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd0000409Bbc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd0000409Abc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd00004091bc*sc*i* cciss
+alias pci:v00000E11d0000B178sv00000E11sd00004083bc*sc*i* cciss
+alias pci:v00000E11d0000B178sv00000E11sd00004082bc*sc*i* cciss
+alias pci:v00000E11d0000B178sv00000E11sd00004080bc*sc*i* cciss
+alias pci:v00000E11d0000B060sv00000E11sd00004070bc*sc*i* cciss
+alias pci:v0000103Cd*sv*sd*bc01sc04i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003356bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003355bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003354bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003353bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003352bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003351bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003350bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003233bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd0000324Bbc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd0000324Abc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003249bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003247bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003245bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003243bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003241bc*sc*i* hpsa
diff --git a/testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.builtin b/testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.builtin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.builtin
diff --git a/testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.order b/testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.order
new file mode 100644
index 0000000..4b64309
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.order
@@ -0,0 +1,7 @@
+#336
+kernel/drivers/block/cciss.ko
+#2094
+kernel/drivers/scsi/scsi_mod.ko
+#2137
+kernel/drivers/scsi/hpsa.ko
+
diff --git a/testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.alias b/testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.alias
new file mode 100644
index 0000000..5675329
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.alias
@@ -0,0 +1,37 @@
+# Aliases extracted from modules themselves.
+alias pci:v0000103Cd00003230sv0000103Csd0000323Dbc*sc*i* cciss
+alias pci:v0000103Cd00003230sv0000103Csd00003237bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003215bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003214bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003213bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003212bc*sc*i* cciss
+alias pci:v0000103Cd00003238sv0000103Csd00003211bc*sc*i* cciss
+alias pci:v0000103Cd00003230sv0000103Csd00003235bc*sc*i* cciss
+alias pci:v0000103Cd00003230sv0000103Csd00003234bc*sc*i* cciss
+alias pci:v0000103Cd00003230sv0000103Csd00003223bc*sc*i* cciss
+alias pci:v0000103Cd00003220sv0000103Csd00003225bc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd0000409Dbc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd0000409Cbc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd0000409Bbc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd0000409Abc*sc*i* cciss
+alias pci:v00000E11d00000046sv00000E11sd00004091bc*sc*i* cciss
+alias pci:v00000E11d0000B178sv00000E11sd00004083bc*sc*i* cciss
+alias pci:v00000E11d0000B178sv00000E11sd00004082bc*sc*i* cciss
+alias pci:v00000E11d0000B178sv00000E11sd00004080bc*sc*i* cciss
+alias pci:v00000E11d0000B060sv00000E11sd00004070bc*sc*i* cciss
+alias pci:v0000103Cd*sv*sd*bc01sc04i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003356bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003355bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003354bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003353bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003352bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003351bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Bsv0000103Csd00003350bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003233bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd0000324Bbc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd0000324Abc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003249bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003247bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003245bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003243bc*sc*i* hpsa
+alias pci:v0000103Cd0000323Asv0000103Csd00003241bc*sc*i* hpsa
diff --git a/testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.dep b/testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.dep
new file mode 100644
index 0000000..ec50ac3
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.dep
@@ -0,0 +1,3 @@
+kernel/drivers/block/cciss.ko:
+kernel/drivers/scsi/scsi_mod.ko:
+kernel/drivers/scsi/hpsa.ko: kernel/drivers/scsi/scsi_mod.ko
diff --git a/testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.builtin b/testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.builtin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.builtin
diff --git a/testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.order b/testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.order
new file mode 100644
index 0000000..4b64309
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.order
@@ -0,0 +1,7 @@
+#336
+kernel/drivers/block/cciss.ko
+#2094
+kernel/drivers/scsi/scsi_mod.ko
+#2137
+kernel/drivers/scsi/hpsa.ko
+
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/external.conf b/testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/external.conf
new file mode 100644
index 0000000..59f46ae
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/external.conf
@@ -0,0 +1 @@
+external 4\.4\..* /lib/modules/external
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/search.conf b/testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/search.conf
new file mode 100644
index 0000000..642e497
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/search.conf
@@ -0,0 +1 @@
+search external foobar foo built-in
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-external-first/lib/modules/4.4.4/correct-modules.dep b/testsuite/rootfs-pristine/test-depmod/search-order-external-first/lib/modules/4.4.4/correct-modules.dep
new file mode 100644
index 0000000..e612900
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-external-first/lib/modules/4.4.4/correct-modules.dep
@@ -0,0 +1 @@
+/lib/modules/external/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/external.conf b/testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/external.conf
new file mode 100644
index 0000000..59f46ae
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/external.conf
@@ -0,0 +1 @@
+external 4\.4\..* /lib/modules/external
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/search.conf b/testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/search.conf
new file mode 100644
index 0000000..5fdb812
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/search.conf
@@ -0,0 +1 @@
+search foobar foo built-in external
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-external-last/lib/modules/4.4.4/correct-modules.dep b/testsuite/rootfs-pristine/test-depmod/search-order-external-last/lib/modules/4.4.4/correct-modules.dep
new file mode 100644
index 0000000..eab3bb0
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-external-last/lib/modules/4.4.4/correct-modules.dep
@@ -0,0 +1 @@
+foobar/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/override.conf b/testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/override.conf
new file mode 100644
index 0000000..fadf811
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/override.conf
@@ -0,0 +1 @@
+override mod-simple 4.4.4 override
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/search.conf b/testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/search.conf
new file mode 100644
index 0000000..289ddcd
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/search.conf
@@ -0,0 +1 @@
+search foo built-in
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-override/lib/modules/4.4.4/correct-modules.dep b/testsuite/rootfs-pristine/test-depmod/search-order-override/lib/modules/4.4.4/correct-modules.dep
new file mode 100644
index 0000000..4c7ea8e
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-override/lib/modules/4.4.4/correct-modules.dep
@@ -0,0 +1 @@
+override/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/etc/depmod.d/search.conf b/testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/etc/depmod.d/search.conf
new file mode 100644
index 0000000..d43973e
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/etc/depmod.d/search.conf
@@ -0,0 +1 @@
+search foobar foo built-in
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/lib/modules/4.4.4/correct-modules.dep b/testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/lib/modules/4.4.4/correct-modules.dep
new file mode 100644
index 0000000..eab3bb0
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/lib/modules/4.4.4/correct-modules.dep
@@ -0,0 +1 @@
+foobar/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-simple/etc/depmod.d/search.conf b/testsuite/rootfs-pristine/test-depmod/search-order-simple/etc/depmod.d/search.conf
new file mode 100644
index 0000000..d17cafd
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-simple/etc/depmod.d/search.conf
@@ -0,0 +1 @@
+search updates built-in
diff --git a/testsuite/rootfs-pristine/test-depmod/search-order-simple/lib/modules/4.4.4/correct-modules.dep b/testsuite/rootfs-pristine/test-depmod/search-order-simple/lib/modules/4.4.4/correct-modules.dep
new file mode 100644
index 0000000..c5bba99
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-depmod/search-order-simple/lib/modules/4.4.4/correct-modules.dep
@@ -0,0 +1 @@
+updates/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias.bin b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin
new file mode 100644
index 0000000..1cbec61
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin
@@ -0,0 +1 @@
+kernel/fake_builtin.ko
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.alias.bin b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.bin b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.bin
new file mode 100644
index 0000000..0423f03
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep.bin b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.devname b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.softdep b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols.bin b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias.bin b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin
new file mode 100644
index 0000000..1cbec61
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin
@@ -0,0 +1 @@
+kernel/fake_builtin.ko
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin.bin b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin.bin
new file mode 100644
index 0000000..0423f03
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep.bin b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.devname b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.softdep b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols.bin b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin
new file mode 100644
index 0000000..1cbec61
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin
@@ -0,0 +1 @@
+kernel/fake_builtin.ko
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..0423f03
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-loaded/correct.txt b/testsuite/rootfs-pristine/test-loaded/correct.txt
new file mode 100644
index 0000000..71c5f7f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/correct.txt
@@ -0,0 +1,2 @@
+Module Size Used by
+btusb 11216 0
diff --git a/testsuite/rootfs-pristine/test-loaded/proc/modules b/testsuite/rootfs-pristine/test-loaded/proc/modules
new file mode 100644
index 0000000..ce468c0
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/proc/modules
@@ -0,0 +1 @@
+btusb 11216 0 - Live 0xffffffffa014a000
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/drivers/usb:btusb b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/drivers/usb:btusb
new file mode 120000
index 0000000..c173837
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/drivers/usb:btusb
@@ -0,0 +1 @@
+../../../bus/usb/drivers/btusb \ No newline at end of file
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/holders/.gitignore b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/holders/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/holders/.gitignore
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/initstate b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/notes/.note.gnu.build-id b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/notes/.note.gnu.build-id
new file mode 100644
index 0000000..0758558
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/notes/.note.gnu.build-id
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/disable_scofix b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/disable_scofix
new file mode 100644
index 0000000..d52e798
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/disable_scofix
@@ -0,0 +1 @@
+N
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/force_scofix b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/force_scofix
new file mode 100644
index 0000000..d52e798
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/force_scofix
@@ -0,0 +1 @@
+N
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_csr b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_csr
new file mode 100644
index 0000000..d52e798
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_csr
@@ -0,0 +1 @@
+N
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_dga b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_dga
new file mode 100644
index 0000000..d52e798
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_dga
@@ -0,0 +1 @@
+N
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_sniffer b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_sniffer
new file mode 100644
index 0000000..d52e798
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_sniffer
@@ -0,0 +1 @@
+N
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/reset b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/reset
new file mode 100644
index 0000000..9bda8c3
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/reset
@@ -0,0 +1 @@
+Y
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/refcnt b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/refcnt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/refcnt
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.bss b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.bss
new file mode 100644
index 0000000..e971686
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.bss
@@ -0,0 +1 @@
+0xffffffffa014c828
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.data b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.data
new file mode 100644
index 0000000..5ccfa09
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.data
@@ -0,0 +1 @@
+0xffffffffa014c040
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.exit.text b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.exit.text
new file mode 100644
index 0000000..446df03
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.exit.text
@@ -0,0 +1 @@
+0xffffffffa014bd7c
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.gnu.linkonce.this_module b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.gnu.linkonce.this_module
new file mode 100644
index 0000000..f91a8fd
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.gnu.linkonce.this_module
@@ -0,0 +1 @@
+0xffffffffa014c600
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.init.text b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.init.text
new file mode 100644
index 0000000..1179bf2
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.init.text
@@ -0,0 +1 @@
+0xffffffffa014e000
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.note.gnu.build-id b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.note.gnu.build-id
new file mode 100644
index 0000000..e08ddab
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.note.gnu.build-id
@@ -0,0 +1 @@
+0xffffffffa014bd90
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata
new file mode 100644
index 0000000..327441b
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata
@@ -0,0 +1 @@
+0xffffffffa014bfe0
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.1 b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.1
new file mode 100644
index 0000000..e96b658
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.1
@@ -0,0 +1 @@
+0xffffffffa014be29
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.8 b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.8
new file mode 100644
index 0000000..d437cbe
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.8
@@ -0,0 +1 @@
+0xffffffffa014bdb8
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.smp_locks b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.smp_locks
new file mode 100644
index 0000000..955e243
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.smp_locks
@@ -0,0 +1 @@
+0xffffffffa014bea8
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.strtab b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.strtab
new file mode 100644
index 0000000..a874fed
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.strtab
@@ -0,0 +1 @@
+0xffffffffa014ede8
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.symtab b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.symtab
new file mode 100644
index 0000000..fafe019
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.symtab
@@ -0,0 +1 @@
+0xffffffffa014e020
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.text b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.text
new file mode 100644
index 0000000..0a967df
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.text
@@ -0,0 +1 @@
+0xffffffffa014a000
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/__param b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/__param
new file mode 100644
index 0000000..9f6710a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/__param
@@ -0,0 +1 @@
+0xffffffffa014bf20
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/srcversion b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/srcversion
new file mode 100644
index 0000000..a688adf
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/srcversion
@@ -0,0 +1 @@
+8EEAC35838CCA1118F97F5F
diff --git a/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/version b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/version
new file mode 100644
index 0000000..5a2a580
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-loaded/sys/module/btusb/version
@@ -0,0 +1 @@
+0.6
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-author.txt b/testsuite/rootfs-pristine/test-modinfo/correct-author.txt
new file mode 100644
index 0000000..b74c9bf
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-author.txt
@@ -0,0 +1,3 @@
+Lucas De Marchi <lucas.demarchi@intel.com>
+Lucas De Marchi <lucas.demarchi@intel.com>
+Lucas De Marchi <lucas.demarchi@intel.com>
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-depends.txt b/testsuite/rootfs-pristine/test-modinfo/correct-depends.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-depends.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-description.txt b/testsuite/rootfs-pristine/test-modinfo/correct-description.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-description.txt
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-external.txt b/testsuite/rootfs-pristine/test-modinfo/correct-external.txt
new file mode 100644
index 0000000..a094abe
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-external.txt
@@ -0,0 +1 @@
+/lib/modules/external/mod-simple.ko
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-filename.txt b/testsuite/rootfs-pristine/test-modinfo/correct-filename.txt
new file mode 100644
index 0000000..bcd47c0
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-filename.txt
@@ -0,0 +1,3 @@
+/mod-simple-i386.ko
+/mod-simple-x86_64.ko
+/mod-simple-sparc64.ko
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-license.txt b/testsuite/rootfs-pristine/test-modinfo/correct-license.txt
new file mode 100644
index 0000000..f79931d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-license.txt
@@ -0,0 +1,4 @@
+LGPL
+LGPL
+LGPL
+
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-parm.txt b/testsuite/rootfs-pristine/test-modinfo/correct-parm.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-parm.txt
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo-openssl.txt b/testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo-openssl.txt
new file mode 100644
index 0000000..f97c4fa
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo-openssl.txt
@@ -0,0 +1,3 @@
+sha1
+sha256
+sha256
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo.txt b/testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo.txt
new file mode 100644
index 0000000..23cb933
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo.txt
@@ -0,0 +1,4 @@
+sha1
+sha256
+unknown
+
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-sig_key-openssl.txt b/testsuite/rootfs-pristine/test-modinfo/correct-sig_key-openssl.txt
new file mode 100644
index 0000000..25a75a8
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-sig_key-openssl.txt
@@ -0,0 +1,3 @@
+E3:C8:FC:A7:3F:B3:1D:DE:84:81:EF:38:E3:4C:DE:4B:0C:FD:1B:F9
+E3:C8:FC:A7:3F:B3:1D:DE:84:81:EF:38:E3:4C:DE:4B:0C:FD:1B:F9
+26:DA:C3:EB:0F:0D:1A:56:A2:D8:B2:13:F0:D7:53:47:1D:0D:48:68
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-sig_key.txt b/testsuite/rootfs-pristine/test-modinfo/correct-sig_key.txt
new file mode 100644
index 0000000..7dc4c6a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-sig_key.txt
@@ -0,0 +1,3 @@
+E3:C8:FC:A7:3F:B3:1D:DE:84:81:EF:38:E3:4C:DE:4B:0C:FD:1B:F9
+E3:C8:FC:A7:3F:B3:1D:DE:84:81:EF:38:E3:4C:DE:4B:0C:FD:1B:F9
+
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-signer-openssl.txt b/testsuite/rootfs-pristine/test-modinfo/correct-signer-openssl.txt
new file mode 100644
index 0000000..2b979f9
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-signer-openssl.txt
@@ -0,0 +1,3 @@
+Magrathea: Glacier signing key
+Magrathea: Glacier signing key
+Build time autogenerated kernel key
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct-signer.txt b/testsuite/rootfs-pristine/test-modinfo/correct-signer.txt
new file mode 100644
index 0000000..afe83df
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct-signer.txt
@@ -0,0 +1,3 @@
+Magrathea: Glacier signing key
+Magrathea: Glacier signing key
+
diff --git a/testsuite/rootfs-pristine/test-modinfo/correct.txt b/testsuite/rootfs-pristine/test-modinfo/correct.txt
new file mode 100644
index 0000000..92ff247
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/correct.txt
@@ -0,0 +1,24 @@
+filename: /ext4-x86_64-sha1.ko
+license: GPL
+description: Fourth Extended Filesystem
+author: Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others
+alias: ext3
+alias: ext2
+depends: mbcache,jbd2
+intree: Y
+vermagic: 3.7.0 SMP mod_unload
+signer: Magrathea: Glacier signing key
+sig_key: E3:C8:FC:A7:3F:B3:1D:DE:84:81:EF:38:E3:4C:DE:4B:0C:FD:1B:F9
+sig_hashalgo: sha1
+filename: /ext4-x86_64-sha256.ko
+license: GPL
+description: Fourth Extended Filesystem
+author: Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others
+alias: ext3
+alias: ext2
+depends: mbcache,jbd2
+intree: Y
+vermagic: 3.7.0 SMP mod_unload
+signer: Magrathea: Glacier signing key
+sig_key: E3:C8:FC:A7:3F:B3:1D:DE:84:81:EF:38:E3:4C:DE:4B:0C:FD:1B:F9
+sig_hashalgo: sha256
diff --git a/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..e612900
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep
@@ -0,0 +1 @@
+/lib/modules/external/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..556e3c8
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.devname
diff --git a/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/correct.txt b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/correct.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/correct.txt
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/etc/modprobe.d/bogus.conf b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/etc/modprobe.d/bogus.conf
new file mode 100644
index 0000000..fe0d1b4
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/etc/modprobe.d/bogus.conf
@@ -0,0 +1 @@
+alias mod-simple deaddood
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..5476653
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep
@@ -0,0 +1 @@
+kernel/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..b09a854
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/alias-to-none/proc/modules b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/proc/modules
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/alias-to-none/proc/modules
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/correct.txt b/testsuite/rootfs-pristine/test-modprobe/builtin/correct.txt
new file mode 100644
index 0000000..509143d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/correct.txt
@@ -0,0 +1 @@
+unix
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin
new file mode 100644
index 0000000..3c4ef2c
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin
@@ -0,0 +1 @@
+kernel/net/unix/unix.ko
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..41af3ee
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..3cc0512
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1,2 @@
+# Soft dependencies extracted from modules themselves.
+# Copy, with a .conf extension, to /etc/modprobe.d to use it with modprobe.
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..e612900
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep
@@ -0,0 +1 @@
+/lib/modules/external/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..556e3c8
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.devname
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/external/proc/modules b/testsuite/rootfs-pristine/test-modprobe/external/proc/modules
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/external/proc/modules
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..5476653
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep
@@ -0,0 +1 @@
+kernel/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..b09a854
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/force/proc/modules b/testsuite/rootfs-pristine/test-modprobe/force/proc/modules
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/force/proc/modules
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/etc/modprobe.d/dumb-instal-loop.conf b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/etc/modprobe.d/dumb-instal-loop.conf
new file mode 100644
index 0000000..4ec3fbf
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/etc/modprobe.d/dumb-instal-loop.conf
@@ -0,0 +1 @@
+install mod-loop-b $MODPROBE --ignore-install mod-loop-b && { $MODPROBE --quiet mod-loop-a ; }
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..869e6ea
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep
@@ -0,0 +1,2 @@
+kernel/mod-loop-b.ko:
+kernel/mod-loop-a.ko: kernel/mod-loop-b.ko
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..3d1b357
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..6c53580
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1,3 @@
+# Aliases for symbols, used by symbol_request().
+alias symbol:printB mod_loop_b
+alias symbol:printA mod_loop_a
diff --git a/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7cb72ca
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..e612900
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep
@@ -0,0 +1 @@
+/lib/modules/external/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..556e3c8
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.devname
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/proc/modules b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/proc/modules
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-abspath/proc/modules
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..e612900
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep
@@ -0,0 +1 @@
+/lib/modules/external/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..556e3c8
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.devname
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/proc/modules b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/proc/modules
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-from-relpath/proc/modules
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/correct.txt
new file mode 100644
index 0000000..0d50154
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/correct.txt
@@ -0,0 +1 @@
+insmod /lib/modules/4.4.4/kernel/mod-simple.ko foo bar=1
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..5476653
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep
@@ -0,0 +1 @@
+kernel/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..b09a854
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/cmdline
new file mode 100644
index 0000000..cd3bacd
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/cmdline
@@ -0,0 +1 @@
+initrd=\initramfs-linux.img mod_simple.foo mod_simple.bar=1 root=/dev/sda2 rootfstype=ext4 add_efi_memmap quiet rw
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/modules b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/modules
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/modules
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/correct.txt
new file mode 100644
index 0000000..b73a680
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/correct.txt
@@ -0,0 +1,5 @@
+options psmouse foo
+options psmouse bar=1
+
+# End of configuration files. Dumping indexes now:
+
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/proc/cmdline
new file mode 100644
index 0000000..f048fdd
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/proc/cmdline
@@ -0,0 +1 @@
+psmouse.foo psmouse.bar=1 quiet rw
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/correct.txt
new file mode 100644
index 0000000..b73a680
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/correct.txt
@@ -0,0 +1,5 @@
+options psmouse foo
+options psmouse bar=1
+
+# End of configuration files. Dumping indexes now:
+
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/proc/cmdline
new file mode 100644
index 0000000..3575bb1
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/proc/cmdline
@@ -0,0 +1 @@
+BOOT_IMAGE=/boot/vmlinuz-3.12.12-57.g5f654cf-default initrd=\initramfs-linux.img psmouse.foo psmouse.bar=1 root=/dev/sda2 rootfstype=ext4 add_efi_memmap quiet rw
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/correct.txt
new file mode 100644
index 0000000..60fa483
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/correct.txt
@@ -0,0 +1,4 @@
+options testmodule testparam=1.5G
+
+# End of configuration files. Dumping indexes now:
+
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/proc/cmdline
new file mode 100644
index 0000000..c460c5e
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/proc/cmdline
@@ -0,0 +1 @@
+testmodule.testparam=1.5G
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/correct.txt
new file mode 100644
index 0000000..6996ba2
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/correct.txt
@@ -0,0 +1,6 @@
+options psmouse foo
+options psmouse bar=1
+options psmouse foobar="test 1"
+
+# End of configuration files. Dumping indexes now:
+
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/proc/cmdline
new file mode 100644
index 0000000..84e0168
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/proc/cmdline
@@ -0,0 +1 @@
+psmouse.foo psmouse.bar=1 psmouse.foobar="test 1" psmouse."invalid option" " notamodule" "noteamodule2 " notamodule3 quiet rw
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/correct.txt
new file mode 100644
index 0000000..7a087ef
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/correct.txt
@@ -0,0 +1,6 @@
+options psmouse foo=2
+options psmouse bar=1
+options psmouse zinga=test
+
+# End of configuration files. Dumping indexes now:
+
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/proc/cmdline
new file mode 100644
index 0000000..14bbd2e
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/proc/cmdline
@@ -0,0 +1 @@
+psmouse.foo=2 ivrs_acpihid[14:00.5]=AMD0020:00 psmouse.bar=1 psmouse.zinga=test
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/correct.txt
new file mode 100644
index 0000000..d80da6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/correct.txt
@@ -0,0 +1,5 @@
+options psmouse foo
+options parport dyndbg="file drivers/parport/ieee1284_ops.c +mpf"
+
+# End of configuration files. Dumping indexes now:
+
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/correct.txt
new file mode 100644
index 0000000..d80da6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/correct.txt
@@ -0,0 +1,5 @@
+options psmouse foo
+options parport dyndbg="file drivers/parport/ieee1284_ops.c +mpf"
+
+# End of configuration files. Dumping indexes now:
+
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/proc/cmdline
new file mode 100644
index 0000000..86f9052
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/proc/cmdline
@@ -0,0 +1 @@
+psmouse.foo parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf" quiet rw
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/proc/cmdline
new file mode 100644
index 0000000..86f9052
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/proc/cmdline
@@ -0,0 +1 @@
+psmouse.foo parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf" quiet rw
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/correct.txt
new file mode 100644
index 0000000..d80da6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/correct.txt
@@ -0,0 +1,5 @@
+options psmouse foo
+options parport dyndbg="file drivers/parport/ieee1284_ops.c +mpf"
+
+# End of configuration files. Dumping indexes now:
+
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/correct.txt
new file mode 100644
index 0000000..d80da6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/correct.txt
@@ -0,0 +1,5 @@
+options psmouse foo
+options parport dyndbg="file drivers/parport/ieee1284_ops.c +mpf"
+
+# End of configuration files. Dumping indexes now:
+
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/proc/cmdline
new file mode 100644
index 0000000..86f9052
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/proc/cmdline
@@ -0,0 +1 @@
+psmouse.foo parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf" quiet rw
diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/proc/cmdline
new file mode 100644
index 0000000..eab04ad
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/proc/cmdline
@@ -0,0 +1 @@
+psmouse.foo "parport.dyndbg=file drivers/parport/ieee1284_ops.c +mpf" quiet rw
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep
new file mode 100644
index 0000000..5476653
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep
@@ -0,0 +1 @@
+kernel/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep.bin
new file mode 100644
index 0000000..b09a854
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.devname b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/proc/modules b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/proc/modules
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel-force/proc/modules
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep
new file mode 100644
index 0000000..5476653
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep
@@ -0,0 +1 @@
+kernel/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep.bin
new file mode 100644
index 0000000..b09a854
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.devname b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/oldkernel/proc/modules b/testsuite/rootfs-pristine/test-modprobe/oldkernel/proc/modules
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/oldkernel/proc/modules
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/correct-mod-simple.txt b/testsuite/rootfs-pristine/test-modprobe/show-depends/correct-mod-simple.txt
new file mode 100644
index 0000000..5426b9b
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/correct-mod-simple.txt
@@ -0,0 +1 @@
+insmod /lib/modules/4.4.4/kernel/mod-simple.ko
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/correct.txt b/testsuite/rootfs-pristine/test-modprobe/show-depends/correct.txt
new file mode 100644
index 0000000..38d6d97
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/correct.txt
@@ -0,0 +1,2 @@
+insmod /lib/modules/4.4.4/kernel/mod-loop-b.ko
+insmod /lib/modules/4.4.4/kernel/mod-loop-a.ko
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..f166f66
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep
@@ -0,0 +1,3 @@
+kernel/mod-simple.ko:
+kernel/mod-loop-b.ko:
+kernel/mod-loop-a.ko: kernel/mod-loop-b.ko
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..418d583
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..6c53580
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1,3 @@
+# Aliases for symbols, used by symbol_request().
+alias symbol:printB mod_loop_b
+alias symbol:printA mod_loop_a
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..27c5f43
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/proc/modules b/testsuite/rootfs-pristine/test-modprobe/show-depends/proc/modules
new file mode 100644
index 0000000..aab95dd
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/proc/modules
@@ -0,0 +1,2 @@
+btusb 11911 0 - Live 0xffffffffa00ec000
+bluetooth 173424 1 btusb, Live 0xffffffffa0040000
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/sys/module/mod_loop_a/initstate b/testsuite/rootfs-pristine/test-modprobe/show-depends/sys/module/mod_loop_a/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/sys/module/mod_loop_a/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-depends/sys/module/mod_loop_b/initstate b/testsuite/rootfs-pristine/test-modprobe/show-depends/sys/module/mod_loop_b/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-depends/sys/module/mod_loop_b/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-modprobe/show-exports/correct.txt b/testsuite/rootfs-pristine/test-modprobe/show-exports/correct.txt
new file mode 100644
index 0000000..0d659ef
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/show-exports/correct.txt
@@ -0,0 +1 @@
+0x[0-9a-fA-F]+ printA
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/etc/modprobe.d/dumb-softdep.conf b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/etc/modprobe.d/dumb-softdep.conf
new file mode 100644
index 0000000..af30b49
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/etc/modprobe.d/dumb-softdep.conf
@@ -0,0 +1 @@
+softdep mod-loop-b post: mod-loop-a
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.builtin.bin
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..869e6ea
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep
@@ -0,0 +1,2 @@
+kernel/mod-loop-b.ko:
+kernel/mod-loop-a.ko: kernel/mod-loop-b.ko
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..3d1b357
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..6c53580
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1,3 @@
+# Aliases for symbols, used by symbol_request().
+alias symbol:printB mod_loop_b
+alias symbol:printA mod_loop_a
diff --git a/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7cb72ca
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-new-module/from_alias/correct.txt b/testsuite/rootfs-pristine/test-new-module/from_alias/correct.txt
new file mode 100644
index 0000000..6ed98ea
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-new-module/from_alias/correct.txt
@@ -0,0 +1,2 @@
+modname: ext4
+
diff --git a/testsuite/rootfs-pristine/test-new-module/from_alias/etc/modprobe.d/modprobe.conf b/testsuite/rootfs-pristine/test-new-module/from_alias/etc/modprobe.d/modprobe.conf
new file mode 100644
index 0000000..f9629a0
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-new-module/from_alias/etc/modprobe.d/modprobe.conf
@@ -0,0 +1 @@
+alias ext4.* ext4
diff --git a/testsuite/rootfs-pristine/test-new-module/from_name/correct.txt b/testsuite/rootfs-pristine/test-new-module/from_name/correct.txt
new file mode 100644
index 0000000..a75cd3b
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-new-module/from_name/correct.txt
@@ -0,0 +1,6 @@
+modname: ext4
+modname: balbalbalbbalbalbalbalbalbalbal
+modname: snd_hda_intel
+modname: snd_timer
+modname: iTCO_wdt
+
diff --git a/testsuite/rootfs-pristine/test-rootfs/lib/modules/a b/testsuite/rootfs-pristine/test-rootfs/lib/modules/a
new file mode 100644
index 0000000..837c960
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-rootfs/lib/modules/a
@@ -0,0 +1 @@
+kmod-test-chroot-works
diff --git a/testsuite/rootfs-pristine/test-rootfs/testdir/.gitignore b/testsuite/rootfs-pristine/test-rootfs/testdir/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-rootfs/testdir/.gitignore
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.builtin b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.builtin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.builtin
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.builtin.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..5476653
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.dep
@@ -0,0 +1 @@
+kernel/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..b09a854
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.order b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.order
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.order
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/insert/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.alias b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.alias
new file mode 100644
index 0000000..ba76e18
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.alias
@@ -0,0 +1 @@
+# Aliases extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.alias.bin b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.alias.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.alias.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.builtin b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.builtin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.builtin
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.builtin.bin b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.builtin.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.builtin.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.dep b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.dep
new file mode 100644
index 0000000..5476653
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.dep
@@ -0,0 +1 @@
+kernel/mod-simple.ko:
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.dep.bin b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.dep.bin
new file mode 100644
index 0000000..b09a854
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.dep.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.devname b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.devname
new file mode 100644
index 0000000..58f6d6d
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.devname
@@ -0,0 +1 @@
+# Device nodes to trigger on-demand module loading.
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.order b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.order
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.order
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.softdep b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.softdep
new file mode 100644
index 0000000..5554ccc
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.softdep
@@ -0,0 +1 @@
+# Soft dependencies extracted from modules themselves.
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.symbols b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.symbols
new file mode 100644
index 0000000..618c345
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.symbols
@@ -0,0 +1 @@
+# Aliases for symbols, used by symbol_request().
diff --git a/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.symbols.bin b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.symbols.bin
new file mode 100644
index 0000000..7075435
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/lib/modules/4.4.4/modules.symbols.bin
Binary files differ
diff --git a/testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/holders/.gitignore b/testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/holders/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/holders/.gitignore
diff --git a/testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/initstate b/testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/initstate
new file mode 100644
index 0000000..e23fe64
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/initstate
@@ -0,0 +1 @@
+live
diff --git a/testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/refcnt b/testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/refcnt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-tools/remove/sys/module/mod_simple/refcnt
@@ -0,0 +1 @@
+0
diff --git a/testsuite/rootfs-pristine/test-util/alias-correct.txt b/testsuite/rootfs-pristine/test-util/alias-correct.txt
new file mode 100644
index 0000000..86d0304
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-util/alias-correct.txt
@@ -0,0 +1,25 @@
+input test1234
+return 0
+len 8
+output test1234
+
+input test[abcfoobar]2211
+return 0
+len 19
+output test[abcfoobar]2211
+
+input bar[aaa][bbbb]sss
+return 0
+len 17
+output bar[aaa][bbbb]sss
+
+input kmod[p.b]lib
+return 0
+len 12
+output kmod[p.b]lib
+
+input [az]1234[AZ]
+return 0
+len 12
+output [az]1234[AZ]
+
diff --git a/testsuite/rootfs-pristine/test-util/freadline_wrapped-correct.txt b/testsuite/rootfs-pristine/test-util/freadline_wrapped-correct.txt
new file mode 100644
index 0000000..80c671b
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-util/freadline_wrapped-correct.txt
@@ -0,0 +1,10 @@
+this is the first line wrapped by one \
+2
+this is a single line
+1
+three line lines in a row
+3
+this line has exactly 256 chars to make fun with the implementation, so it must have word after word after word after word after word after word after word after word after word after word after word after word after word after word until it hits 256 chars
+1
+this line is very large because it has word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word until it hits 2015 chars
+1
diff --git a/testsuite/rootfs-pristine/test-util/freadline_wrapped-input.txt b/testsuite/rootfs-pristine/test-util/freadline_wrapped-input.txt
new file mode 100644
index 0000000..1c81f91
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-util/freadline_wrapped-input.txt
@@ -0,0 +1,8 @@
+this is the first line \
+wrapped by one \\
+this is a single line
+three line \
+lines \
+in a row
+this line has exactly 256 chars to make fun with the implementation, so it must have word after word after word after word after word after word after word after word after word after word after word after word after word after word until it hits 256 chars
+this line is very large because it has word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word after word until it hits 2015 chars
diff --git a/testsuite/rootfs-pristine/test-util2/write-str-safe-correct.txt b/testsuite/rootfs-pristine/test-util2/write-str-safe-correct.txt
new file mode 100644
index 0000000..30d74d2
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-util2/write-str-safe-correct.txt
@@ -0,0 +1 @@
+test \ No newline at end of file
diff --git a/testsuite/setup-rootfs.sh b/testsuite/setup-rootfs.sh
new file mode 100755
index 0000000..a780f93
--- /dev/null
+++ b/testsuite/setup-rootfs.sh
@@ -0,0 +1,181 @@
+#!/bin/bash
+
+set -e
+
+ROOTFS_PRISTINE=$1
+ROOTFS=$2
+MODULE_PLAYGROUND=$3
+CONFIG_H=$4
+SYSCONFDIR=$5
+
+# create rootfs from rootfs-pristine
+
+create_rootfs() {
+ rm -rf "$ROOTFS"
+ mkdir -p $(dirname "$ROOTFS")
+ cp -r "$ROOTFS_PRISTINE" "$ROOTFS"
+ find "$ROOTFS" -type d -exec chmod +w {} \;
+ find "$ROOTFS" -type f -name .gitignore -exec rm -f {} \;
+ if [ "$MODULE_DIRECTORY" != "/lib/modules" ] ; then
+ sed -i -e "s|/lib/modules|$MODULE_DIRECTORY|g" $(find "$ROOTFS" -name \*.txt -o -name \*.conf -o -name \*.dep)
+ sed -i -e "s|$MODULE_DIRECTORY/external|/lib/modules/external|g" $(find "$ROOTFS" -name \*.txt -o -name \*.conf -o -name \*.dep)
+ for i in "$ROOTFS"/*/lib/modules/* "$ROOTFS"/*/*/lib/modules/* ; do
+ version="$(basename $i)"
+ [ $version != 'external' ] || continue
+ mod="$(dirname $i)"
+ lib="$(dirname $mod)"
+ up="$(dirname $lib)$MODULE_DIRECTORY"
+ mkdir -p "$up"
+ mv "$i" "$up"
+ done
+ fi
+
+ if [ "$SYSCONFDIR" != "/etc" ]; then
+ find "$ROOTFS" -type d -name etc -printf "%h\n" | while read -r e; do
+ mkdir -p "$(dirname $e/$SYSCONFDIR)"
+ mv $e/{etc,$SYSCONFDIR}
+ done
+ fi
+}
+
+feature_enabled() {
+ local feature=$1
+ grep KMOD_FEATURES $CONFIG_H | head -n 1 | grep -q \+$feature
+}
+
+declare -A map
+map=(
+ ["test-depmod/search-order-simple$MODULE_DIRECTORY/4.4.4/kernel/crypto/"]="mod-simple.ko"
+ ["test-depmod/search-order-simple$MODULE_DIRECTORY/4.4.4/updates/"]="mod-simple.ko"
+ ["test-depmod/search-order-same-prefix$MODULE_DIRECTORY/4.4.4/foo/"]="mod-simple.ko"
+ ["test-depmod/search-order-same-prefix$MODULE_DIRECTORY/4.4.4/foobar/"]="mod-simple.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-a.ko"]="mod-loop-a.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-b.ko"]="mod-loop-b.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-c.ko"]="mod-loop-c.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-d.ko"]="mod-loop-d.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-e.ko"]="mod-loop-e.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-f.ko"]="mod-loop-f.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-g.ko"]="mod-loop-g.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-h.ko"]="mod-loop-h.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-i.ko"]="mod-loop-i.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-j.ko"]="mod-loop-j.ko"
+ ["test-depmod/detect-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-k.ko"]="mod-loop-k.ko"
+ ["test-depmod/search-order-external-first$MODULE_DIRECTORY/4.4.4/foo/"]="mod-simple.ko"
+ ["test-depmod/search-order-external-first$MODULE_DIRECTORY/4.4.4/foobar/"]="mod-simple.ko"
+ ["test-depmod/search-order-external-first/lib/modules/external/"]="mod-simple.ko"
+ ["test-depmod/search-order-external-last$MODULE_DIRECTORY/4.4.4/foo/"]="mod-simple.ko"
+ ["test-depmod/search-order-external-last$MODULE_DIRECTORY/4.4.4/foobar/"]="mod-simple.ko"
+ ["test-depmod/search-order-external-last/lib/modules/external/"]="mod-simple.ko"
+ ["test-depmod/search-order-override$MODULE_DIRECTORY/4.4.4/foo/"]="mod-simple.ko"
+ ["test-depmod/search-order-override$MODULE_DIRECTORY/4.4.4/override/"]="mod-simple.ko"
+ ["test-dependencies$MODULE_DIRECTORY/4.0.20-kmod/kernel/fs/foo/"]="mod-foo-b.ko"
+ ["test-dependencies$MODULE_DIRECTORY/4.0.20-kmod/kernel/"]="mod-foo-c.ko"
+ ["test-dependencies$MODULE_DIRECTORY/4.0.20-kmod/kernel/lib/"]="mod-foo-a.ko"
+ ["test-dependencies$MODULE_DIRECTORY/4.0.20-kmod/kernel/fs/"]="mod-foo.ko"
+ ["test-init/"]="mod-simple.ko"
+ ["test-remove/"]="mod-simple.ko"
+ ["test-modprobe/show-depends$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-a.ko"]="mod-loop-a.ko"
+ ["test-modprobe/show-depends$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-b.ko"]="mod-loop-b.ko"
+ ["test-modprobe/show-depends$MODULE_DIRECTORY/4.4.4/kernel/mod-simple.ko"]="mod-simple.ko"
+ ["test-modprobe/show-exports/mod-loop-a.ko"]="mod-loop-a.ko"
+ ["test-modprobe/softdep-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-a.ko"]="mod-loop-a.ko"
+ ["test-modprobe/softdep-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-b.ko"]="mod-loop-b.ko"
+ ["test-modprobe/install-cmd-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-a.ko"]="mod-loop-a.ko"
+ ["test-modprobe/install-cmd-loop$MODULE_DIRECTORY/4.4.4/kernel/mod-loop-b.ko"]="mod-loop-b.ko"
+ ["test-modprobe/force$MODULE_DIRECTORY/4.4.4/kernel/"]="mod-simple.ko"
+ ["test-modprobe/oldkernel$MODULE_DIRECTORY/3.3.3/kernel/"]="mod-simple.ko"
+ ["test-modprobe/oldkernel-force$MODULE_DIRECTORY/3.3.3/kernel/"]="mod-simple.ko"
+ ["test-modprobe/alias-to-none$MODULE_DIRECTORY/4.4.4/kernel/"]="mod-simple.ko"
+ ["test-modprobe/module-param-kcmdline$MODULE_DIRECTORY/4.4.4/kernel/"]="mod-simple.ko"
+ ["test-modprobe/external/lib/modules/external/"]="mod-simple.ko"
+ ["test-modprobe/module-from-abspath/home/foo/"]="mod-simple.ko"
+ ["test-modprobe/module-from-relpath/home/foo/"]="mod-simple.ko"
+ ["test-depmod/modules-order-compressed$MODULE_DIRECTORY/4.4.4/kernel/drivers/block/cciss.ko"]="mod-fake-cciss.ko"
+ ["test-depmod/modules-order-compressed$MODULE_DIRECTORY/4.4.4/kernel/drivers/scsi/hpsa.ko"]="mod-fake-hpsa.ko"
+ ["test-depmod/modules-order-compressed$MODULE_DIRECTORY/4.4.4/kernel/drivers/scsi/scsi_mod.ko"]="mod-fake-scsi-mod.ko"
+ ["test-depmod/modules-outdir$MODULE_DIRECTORY/4.4.4/kernel/drivers/block/cciss.ko"]="mod-fake-cciss.ko"
+ ["test-depmod/modules-outdir$MODULE_DIRECTORY/4.4.4/kernel/drivers/scsi/hpsa.ko"]="mod-fake-hpsa.ko"
+ ["test-depmod/modules-outdir$MODULE_DIRECTORY/4.4.4/kernel/drivers/scsi/scsi_mod.ko"]="mod-fake-scsi-mod.ko"
+ ["test-modinfo/mod-simple-i386.ko"]="mod-simple-i386.ko"
+ ["test-modinfo/mod-simple-x86_64.ko"]="mod-simple-x86_64.ko"
+ ["test-modinfo/mod-simple-sparc64.ko"]="mod-simple-sparc64.ko"
+ ["test-modinfo/mod-simple-sha1.ko"]="mod-simple.ko"
+ ["test-modinfo/mod-simple-sha256.ko"]="mod-simple.ko"
+ ["test-modinfo/mod-simple-pkcs7.ko"]="mod-simple.ko"
+ ["test-modinfo/external/lib/modules/external/mod-simple.ko"]="mod-simple.ko"
+ ["test-tools/insert$MODULE_DIRECTORY/4.4.4/kernel/"]="mod-simple.ko"
+ ["test-tools/remove$MODULE_DIRECTORY/4.4.4/kernel/"]="mod-simple.ko"
+)
+
+gzip_array=(
+ "test-depmod/modules-order-compressed$MODULE_DIRECTORY/4.4.4/kernel/drivers/block/cciss.ko"
+ )
+
+xz_array=(
+ "test-depmod/modules-order-compressed$MODULE_DIRECTORY/4.4.4/kernel/drivers/scsi/scsi_mod.ko"
+ )
+
+zstd_array=(
+ "test-depmod/modules-order-compressed$MODULE_DIRECTORY/4.4.4/kernel/drivers/scsi/hpsa.ko"
+ )
+
+attach_sha256_array=(
+ "test-modinfo/mod-simple-sha256.ko"
+ )
+
+attach_sha1_array=(
+ "test-modinfo/mod-simple-sha1.ko"
+ )
+
+attach_pkcs7_array=(
+ "test-modinfo/mod-simple-pkcs7.ko"
+ )
+
+create_rootfs
+
+for k in "${!map[@]}"; do
+ dst=${ROOTFS}/$k
+ src=${MODULE_PLAYGROUND}/${map[$k]}
+
+ if [[ $dst = */ ]]; then
+ install -d "$dst"
+ install -t "$dst" "$src"
+ else
+ install -D "$src" "$dst"
+ fi
+done
+
+# start poking the final rootfs...
+
+# compress modules with each format if feature is enabled
+if feature_enabled ZLIB; then
+ for m in "${gzip_array[@]}"; do
+ gzip "$ROOTFS/$m"
+ done
+fi
+
+if feature_enabled XZ; then
+ for m in "${xz_array[@]}"; do
+ xz "$ROOTFS/$m"
+ done
+fi
+
+if feature_enabled ZSTD; then
+ for m in "${zstd_array[@]}"; do
+ zstd --rm $ROOTFS/$m
+ done
+fi
+
+for m in "${attach_sha1_array[@]}"; do
+ cat "${MODULE_PLAYGROUND}/dummy.sha1" >>"${ROOTFS}/$m"
+done
+
+for m in "${attach_sha256_array[@]}"; do
+ cat "${MODULE_PLAYGROUND}/dummy.sha256" >>"${ROOTFS}/$m"
+done
+
+for m in "${attach_pkcs7_array[@]}"; do
+ cat "${MODULE_PLAYGROUND}/dummy.pkcs7" >>"${ROOTFS}/$m"
+done
+
+touch testsuite/stamp-rootfs
diff --git a/testsuite/stripped-module.h b/testsuite/stripped-module.h
new file mode 100644
index 0000000..83986b2
--- /dev/null
+++ b/testsuite/stripped-module.h
@@ -0,0 +1,29 @@
+#pragma once
+
+enum module_state
+{
+ MODULE_STATE_LIVE,
+ MODULE_STATE_COMING,
+ MODULE_STATE_GOING,
+};
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define MODULE_NAME_LEN (64 - sizeof(unsigned long))
+
+struct module
+{
+ enum module_state state;
+
+ /* Member of list of modules */
+ struct list_head list;
+
+ /* Unique handle for this module */
+ char name[MODULE_NAME_LEN];
+};
+
+/* padding */
+#define MODULE_NAME_OFFSET_64 4 + 4 + 2 * 8
+#define MODULE_NAME_OFFSET_32 4 + 2 * 4
diff --git a/testsuite/test-array.c b/testsuite/test-array.c
new file mode 100644
index 0000000..ef1e1e9
--- /dev/null
+++ b/testsuite/test-array.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <shared/array.h>
+
+#include "testsuite.h"
+
+static int test_array_append1(const struct test *t)
+{
+ struct array array;
+ const char *c1 = "test1";
+
+ array_init(&array, 2);
+ array_append(&array, c1);
+ assert_return(array.count == 1, EXIT_FAILURE);
+ assert_return(array.array[0] == c1, EXIT_FAILURE);
+ array_free_array(&array);
+
+ return 0;
+}
+DEFINE_TEST(test_array_append1,
+ .description = "test simple array append");
+
+
+static int test_array_append2(const struct test *t)
+{
+ struct array array;
+ const char *c1 = "test1";
+ const char *c2 = "test2";
+ const char *c3 = "test3";
+
+ array_init(&array, 2);
+ array_append(&array, c1);
+ array_append(&array, c2);
+ array_append(&array, c3);
+ assert_return(array.count == 3, EXIT_FAILURE);
+ assert_return(array.array[0] == c1, EXIT_FAILURE);
+ assert_return(array.array[1] == c2, EXIT_FAILURE);
+ assert_return(array.array[2] == c3, EXIT_FAILURE);
+ array_free_array(&array);
+
+ return 0;
+}
+DEFINE_TEST(test_array_append2,
+ .description = "test array append over step");
+
+static int test_array_append_unique(const struct test *t)
+{
+ struct array array;
+ const char *c1 = "test1";
+ const char *c2 = "test2";
+ const char *c3 = "test3";
+
+ array_init(&array, 2);
+ array_append_unique(&array, c1);
+ array_append_unique(&array, c2);
+ array_append_unique(&array, c3);
+ array_append_unique(&array, c3);
+ array_append_unique(&array, c2);
+ array_append_unique(&array, c1);
+ assert_return(array.count == 3, EXIT_FAILURE);
+ assert_return(array.array[0] == c1, EXIT_FAILURE);
+ assert_return(array.array[1] == c2, EXIT_FAILURE);
+ assert_return(array.array[2] == c3, EXIT_FAILURE);
+ array_free_array(&array);
+
+ return 0;
+}
+DEFINE_TEST(test_array_append_unique,
+ .description = "test array append unique");
+
+static int strptrcmp(const void *pa, const void *pb) {
+ const char *a = *(const char **)pa;
+ const char *b = *(const char **)pb;
+
+ return strcmp(a, b);
+}
+
+static int test_array_sort(const struct test *t)
+{
+ struct array array;
+ const char *c1 = "test1";
+ const char *c2 = "test2";
+ const char *c3 = "test3";
+
+ array_init(&array, 2);
+ array_append(&array, c1);
+ array_append(&array, c2);
+ array_append(&array, c3);
+ array_append(&array, c2);
+ array_append(&array, c3);
+ array_append(&array, c1);
+ array_sort(&array, strptrcmp);
+ assert_return(array.count == 6, EXIT_FAILURE);
+ assert_return(array.array[0] == c1, EXIT_FAILURE);
+ assert_return(array.array[1] == c1, EXIT_FAILURE);
+ assert_return(array.array[2] == c2, EXIT_FAILURE);
+ assert_return(array.array[3] == c2, EXIT_FAILURE);
+ assert_return(array.array[4] == c3, EXIT_FAILURE);
+ assert_return(array.array[5] == c3, EXIT_FAILURE);
+ array_free_array(&array);
+
+ return 0;
+}
+DEFINE_TEST(test_array_sort,
+ .description = "test array sort");
+
+static int test_array_remove_at(const struct test *t)
+{
+ struct array array;
+ const char *c1 = "test1";
+ const char *c2 = "test2";
+ const char *c3 = "test3";
+
+ array_init(&array, 2);
+ array_append(&array, c1);
+ array_append(&array, c2);
+ array_append(&array, c3);
+
+ array_remove_at(&array, 2);
+ assert_return(array.count == 2, EXIT_FAILURE);
+ assert_return(array.array[0] == c1, EXIT_FAILURE);
+ assert_return(array.array[1] == c2, EXIT_FAILURE);
+
+ array_remove_at(&array, 0);
+ assert_return(array.count == 1, EXIT_FAILURE);
+ assert_return(array.array[0] == c2, EXIT_FAILURE);
+
+ array_remove_at(&array, 0);
+ assert_return(array.count == 0, EXIT_FAILURE);
+
+ array_append(&array, c1);
+ array_append(&array, c2);
+ array_append(&array, c3);
+
+ array_remove_at(&array, 1);
+ assert_return(array.count == 2, EXIT_FAILURE);
+ assert_return(array.array[0] == c1, EXIT_FAILURE);
+ assert_return(array.array[1] == c3, EXIT_FAILURE);
+
+ array_free_array(&array);
+
+ return 0;
+}
+DEFINE_TEST(test_array_remove_at,
+ .description = "test array remove at");
+
+static int test_array_pop(const struct test *t)
+{
+ struct array array;
+ const char *c1 = "test1";
+ const char *c2 = "test2";
+ const char *c3 = "test3";
+
+ array_init(&array, 2);
+ array_append(&array, c1);
+ array_append(&array, c2);
+ array_append(&array, c3);
+
+
+ array_pop(&array);
+
+ assert_return(array.count == 2, EXIT_FAILURE);
+ assert_return(array.array[0] == c1, EXIT_FAILURE);
+ assert_return(array.array[1] == c2, EXIT_FAILURE);
+
+ array_pop(&array);
+ array_pop(&array);
+
+ assert_return(array.count == 0, EXIT_FAILURE);
+
+ array_free_array(&array);
+
+ return 0;
+}
+
+DEFINE_TEST(test_array_pop,
+ .description = "test array pop");
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-blacklist.c b/testsuite/test-blacklist.c
new file mode 100644
index 0000000..969567d
--- /dev/null
+++ b/testsuite/test-blacklist.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <shared/util.h>
+
+#include <libkmod/libkmod.h>
+
+/* good luck bulding a kmod_list outside of the library... makes this blacklist
+ * function rather pointless */
+#include <libkmod/libkmod-internal.h>
+
+/* FIXME: hack, change name so we don't clash */
+#undef ERR
+#include "testsuite.h"
+
+static int blacklist_1(const struct test *t)
+{
+ struct kmod_ctx *ctx;
+ struct kmod_list *list = NULL, *l, *filtered;
+ struct kmod_module *mod;
+ int err;
+ size_t len = 0;
+
+ const char *names[] = { "pcspkr", "pcspkr2", "floppy", "ext4", NULL };
+ const char **name;
+
+ ctx = kmod_new(NULL, NULL);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ for(name = names; *name; name++) {
+ err = kmod_module_new_from_name(ctx, *name, &mod);
+ if (err < 0)
+ goto fail_lookup;
+ list = kmod_list_append(list, mod);
+ }
+
+ err = kmod_module_apply_filter(ctx, KMOD_FILTER_BLACKLIST, list,
+ &filtered);
+ if (err < 0) {
+ ERR("Could not filter: %s\n", strerror(-err));
+ goto fail;
+ }
+ if (filtered == NULL) {
+ ERR("All modules were filtered out!\n");
+ goto fail;
+ }
+
+ kmod_list_foreach(l, filtered) {
+ const char *modname;
+ mod = kmod_module_get_module(l);
+ modname = kmod_module_get_name(mod);
+ if (streq("pcspkr", modname) || streq("floppy", modname))
+ goto fail;
+ len++;
+ kmod_module_unref(mod);
+ }
+
+ if (len != 2)
+ goto fail;
+
+ kmod_module_unref_list(filtered);
+ kmod_module_unref_list(list);
+ kmod_unref(ctx);
+
+ return EXIT_SUCCESS;
+
+fail:
+ kmod_module_unref_list(list);
+fail_lookup:
+ kmod_unref(ctx);
+ return EXIT_FAILURE;
+}
+
+DEFINE_TEST(blacklist_1,
+ .description = "check if modules are correctly blacklisted",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-blacklist/",
+ },
+ .need_spawn = true,
+);
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-dependencies.c b/testsuite/test-dependencies.c
new file mode 100644
index 0000000..38f5fc1
--- /dev/null
+++ b/testsuite/test-dependencies.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <shared/util.h>
+
+#include <libkmod/libkmod.h>
+
+#include "testsuite.h"
+
+#define TEST_UNAME "4.0.20-kmod"
+
+static noreturn int test_dependencies(const struct test *t)
+{
+ struct kmod_ctx *ctx;
+ struct kmod_module *mod = NULL;
+ struct kmod_list *list, *l;
+ int err;
+ size_t len = 0;
+ int fooa = 0, foob = 0, fooc = 0;
+
+ ctx = kmod_new(NULL, NULL);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ err = kmod_module_new_from_name(ctx, "mod-foo", &mod);
+ if (err < 0 || mod == NULL) {
+ kmod_unref(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ list = kmod_module_get_dependencies(mod);
+
+ kmod_list_foreach(l, list) {
+ struct kmod_module *m = kmod_module_get_module(l);
+ const char *name = kmod_module_get_name(m);
+
+ if (streq(name, "mod_foo_a"))
+ fooa = 1;
+ if (streq(name, "mod_foo_b"))
+ foob = 1;
+ else if (streq(name, "mod_foo_c"))
+ fooc = 1;
+
+ fprintf(stderr, "name=%s", name);
+ kmod_module_unref(m);
+ len++;
+ }
+
+ /* fooa, foob, fooc */
+ if (len != 3 || !fooa || !foob || !fooc)
+ exit(EXIT_FAILURE);
+
+ kmod_module_unref_list(list);
+ kmod_module_unref(mod);
+ kmod_unref(ctx);
+
+ exit(EXIT_SUCCESS);
+}
+DEFINE_TEST(test_dependencies,
+ .description = "test if kmod_module_get_dependencies works",
+ .config = {
+ [TC_UNAME_R] = TEST_UNAME,
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-dependencies/",
+ },
+ .need_spawn = true);
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-depmod.c b/testsuite/test-depmod.c
new file mode 100644
index 0000000..c96dbf0
--- /dev/null
+++ b/testsuite/test-depmod.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "testsuite.h"
+
+#define MODULES_UNAME "4.4.4"
+#define MODULES_ORDER_ROOTFS TESTSUITE_ROOTFS "test-depmod/modules-order-compressed"
+#define MODULES_ORDER_LIB_MODULES MODULES_ORDER_ROOTFS MODULE_DIRECTORY "/" MODULES_UNAME
+static noreturn int depmod_modules_order_for_compressed(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/depmod";
+ const char *const args[] = {
+ progname,
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+
+DEFINE_TEST(depmod_modules_order_for_compressed,
+ .description = "check if depmod let aliases in right order when using compressed modules",
+ .config = {
+ [TC_UNAME_R] = MODULES_UNAME,
+ [TC_ROOTFS] = MODULES_ORDER_ROOTFS,
+ },
+ .output = {
+ .files = (const struct keyval[]) {
+ { MODULES_ORDER_LIB_MODULES "/correct-modules.alias",
+ MODULES_ORDER_LIB_MODULES "/modules.alias" },
+ { }
+ },
+ });
+
+#define MODULES_OUTDIR_ROOTFS TESTSUITE_ROOTFS "test-depmod/modules-outdir"
+#define MODULES_OUTDIR_LIB_MODULES_OUTPUT MODULES_OUTDIR_ROOTFS "/outdir" MODULE_DIRECTORY "/" MODULES_UNAME
+#define MODULES_OUTDIR_LIB_MODULES_INPUT MODULES_OUTDIR_ROOTFS MODULE_DIRECTORY "/" MODULES_UNAME
+static noreturn int depmod_modules_outdir(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/depmod";
+ const char *const args[] = {
+ progname,
+ "--outdir", MODULES_OUTDIR_ROOTFS "/outdir/",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+
+DEFINE_TEST(depmod_modules_outdir,
+ .description = "check if depmod honours the outdir option",
+ .config = {
+ [TC_UNAME_R] = MODULES_UNAME,
+ [TC_ROOTFS] = MODULES_OUTDIR_ROOTFS,
+ },
+ .output = {
+ .files = (const struct keyval[]) {
+ { MODULES_OUTDIR_LIB_MODULES_OUTPUT "/modules.dep",
+ MODULES_OUTDIR_ROOTFS "/correct-modules.dep" },
+ { MODULES_OUTDIR_LIB_MODULES_OUTPUT "/modules.alias",
+ MODULES_OUTDIR_ROOTFS "/correct-modules.alias" },
+ { }
+ },
+ });
+
+#define SEARCH_ORDER_SIMPLE_ROOTFS TESTSUITE_ROOTFS "test-depmod/search-order-simple"
+#define SEARCH_ORDER_SIMPLE_LIB_MODULES SEARCH_ORDER_SIMPLE_ROOTFS MODULE_DIRECTORY "/" MODULES_UNAME
+static noreturn int depmod_search_order_simple(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/depmod";
+ const char *const args[] = {
+ progname,
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(depmod_search_order_simple,
+ .description = "check if depmod honor search order in config",
+ .config = {
+ [TC_UNAME_R] = MODULES_UNAME,
+ [TC_ROOTFS] = SEARCH_ORDER_SIMPLE_ROOTFS,
+ },
+ .output = {
+ .files = (const struct keyval[]) {
+ { SEARCH_ORDER_SIMPLE_LIB_MODULES "/correct-modules.dep",
+ SEARCH_ORDER_SIMPLE_LIB_MODULES "/modules.dep" },
+ { }
+ },
+ });
+
+#define SEARCH_ORDER_SAME_PREFIX_ROOTFS TESTSUITE_ROOTFS "test-depmod/search-order-same-prefix"
+#define SEARCH_ORDER_SAME_PREFIX_LIB_MODULES SEARCH_ORDER_SAME_PREFIX_ROOTFS MODULE_DIRECTORY "/" MODULES_UNAME
+static noreturn int depmod_search_order_same_prefix(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/depmod";
+ const char *const args[] = {
+ progname,
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(depmod_search_order_same_prefix,
+ .description = "check if depmod honor search order in config with same prefix",
+ .config = {
+ [TC_UNAME_R] = MODULES_UNAME,
+ [TC_ROOTFS] = SEARCH_ORDER_SAME_PREFIX_ROOTFS,
+ },
+ .output = {
+ .files = (const struct keyval[]) {
+ { SEARCH_ORDER_SAME_PREFIX_LIB_MODULES "/correct-modules.dep",
+ SEARCH_ORDER_SAME_PREFIX_LIB_MODULES "/modules.dep" },
+ { }
+ },
+ });
+
+#define DETECT_LOOP_ROOTFS TESTSUITE_ROOTFS "test-depmod/detect-loop"
+static noreturn int depmod_detect_loop(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/depmod";
+ const char *const args[] = {
+ progname,
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(depmod_detect_loop,
+ .description = "check if depmod detects module loops correctly",
+ .config = {
+ [TC_UNAME_R] = MODULES_UNAME,
+ [TC_ROOTFS] = DETECT_LOOP_ROOTFS,
+ },
+ .expected_fail = true,
+ .output = {
+ .err = DETECT_LOOP_ROOTFS "/correct.txt",
+ });
+
+#define SEARCH_ORDER_EXTERNAL_FIRST_ROOTFS TESTSUITE_ROOTFS "test-depmod/search-order-external-first"
+#define SEARCH_ORDER_EXTERNAL_FIRST_LIB_MODULES SEARCH_ORDER_EXTERNAL_FIRST_ROOTFS MODULE_DIRECTORY "/" MODULES_UNAME
+static noreturn int depmod_search_order_external_first(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/depmod";
+ const char *const args[] = {
+ progname,
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(depmod_search_order_external_first,
+ .description = "check if depmod honor external keyword with higher priority",
+ .config = {
+ [TC_UNAME_R] = MODULES_UNAME,
+ [TC_ROOTFS] = SEARCH_ORDER_EXTERNAL_FIRST_ROOTFS,
+ },
+ .output = {
+ .files = (const struct keyval[]) {
+ { SEARCH_ORDER_EXTERNAL_FIRST_LIB_MODULES "/correct-modules.dep",
+ SEARCH_ORDER_EXTERNAL_FIRST_LIB_MODULES "/modules.dep" },
+ { }
+ },
+ });
+
+#define SEARCH_ORDER_EXTERNAL_LAST_ROOTFS TESTSUITE_ROOTFS "test-depmod/search-order-external-last"
+#define SEARCH_ORDER_EXTERNAL_LAST_LIB_MODULES SEARCH_ORDER_EXTERNAL_LAST_ROOTFS MODULE_DIRECTORY "/" MODULES_UNAME
+static noreturn int depmod_search_order_external_last(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/depmod";
+ const char *const args[] = {
+ progname,
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(depmod_search_order_external_last,
+ .description = "check if depmod honor external keyword with lower priority",
+ .config = {
+ [TC_UNAME_R] = MODULES_UNAME,
+ [TC_ROOTFS] = SEARCH_ORDER_EXTERNAL_LAST_ROOTFS,
+ },
+ .output = {
+ .files = (const struct keyval[]) {
+ { SEARCH_ORDER_EXTERNAL_LAST_LIB_MODULES "/correct-modules.dep",
+ SEARCH_ORDER_EXTERNAL_LAST_LIB_MODULES "/modules.dep" },
+ { }
+ },
+ });
+
+#define SEARCH_ORDER_OVERRIDE_ROOTFS TESTSUITE_ROOTFS "test-depmod/search-order-override"
+#define SEARCH_ORDER_OVERRIDE_LIB_MODULES SEARCH_ORDER_OVERRIDE_ROOTFS MODULE_DIRECTORY "/" MODULES_UNAME
+static noreturn int depmod_search_order_override(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/depmod";
+ const char *const args[] = {
+ progname,
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(depmod_search_order_override,
+ .description = "check if depmod honor override keyword",
+ .config = {
+ [TC_UNAME_R] = MODULES_UNAME,
+ [TC_ROOTFS] = SEARCH_ORDER_OVERRIDE_ROOTFS,
+ },
+ .output = {
+ .files = (const struct keyval[]) {
+ { SEARCH_ORDER_OVERRIDE_LIB_MODULES "/correct-modules.dep",
+ SEARCH_ORDER_OVERRIDE_LIB_MODULES "/modules.dep" },
+ { }
+ },
+ });
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-hash.c b/testsuite/test-hash.c
new file mode 100644
index 0000000..1bea04e
--- /dev/null
+++ b/testsuite/test-hash.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <shared/hash.h>
+#include <shared/util.h>
+
+#include "testsuite.h"
+
+static int freecount;
+
+static void countfreecalls(void *v)
+{
+ freecount++;
+}
+
+static int test_hash_new(const struct test *t)
+{
+ struct hash *h = hash_new(8, NULL);
+ assert_return(h != NULL, EXIT_FAILURE);
+ hash_free(h);
+ return 0;
+}
+DEFINE_TEST(test_hash_new,
+ .description = "test hash_new");
+
+
+static int test_hash_get_count(const struct test *t)
+{
+ struct hash *h = hash_new(8, NULL);
+ const char *k1 = "k1", *k2 = "k2", *k3 = "k3";
+ const char *v1 = "v1", *v2 = "v2", *v3 = "v3";
+
+ hash_add(h, k1, v1);
+ hash_add(h, k2, v2);
+ hash_add(h, k3, v3);
+
+ assert_return(hash_get_count(h) == 3, EXIT_FAILURE);
+
+ hash_free(h);
+ return 0;
+}
+DEFINE_TEST(test_hash_get_count,
+ .description = "test hash_add / hash_get_count");
+
+
+static int test_hash_replace(const struct test *t)
+{
+ struct hash *h = hash_new(8, countfreecalls);
+ const char *k1 = "k1", *k2 = "k2", *k3 = "k3";
+ const char *v1 = "v1", *v2 = "v2", *v3 = "v3", *v4 = "v4";
+ const char *v;
+ int r = 0;
+
+ r |= hash_add(h, k1, v1);
+ r |= hash_add(h, k2, v2);
+ r |= hash_add(h, k3, v3);
+
+ /* replace v1 */
+ r |= hash_add(h, k1, v4);
+
+ assert_return(r == 0, EXIT_FAILURE);
+ assert_return(hash_get_count(h) == 3, EXIT_FAILURE);
+
+ v = hash_find(h, "k1");
+ assert_return(streq(v, v4), EXIT_FAILURE);
+
+ assert_return(freecount == 1, EXIT_FAILURE);
+
+ hash_free(h);
+ return 0;
+}
+DEFINE_TEST(test_hash_replace,
+ .description = "test hash_add replacing existing value");
+
+
+static int test_hash_replace_failing(const struct test *t)
+{
+ struct hash *h = hash_new(8, countfreecalls);
+ const char *k1 = "k1", *k2 = "k2", *k3 = "k3";
+ const char *v1 = "v1", *v2 = "v2", *v3 = "v3", *v4 = "v4";
+ const char *v;
+ int r = 0;
+
+ r |= hash_add(h, k1, v1);
+ r |= hash_add(h, k2, v2);
+ r |= hash_add(h, k3, v3);
+
+ assert_return(r == 0, EXIT_FAILURE);
+
+ /* replace v1 */
+ r = hash_add_unique(h, k1, v4);
+ assert_return(r != 0, EXIT_FAILURE);
+ assert_return(hash_get_count(h) == 3, EXIT_FAILURE);
+
+ v = hash_find(h, "k1");
+ assert_return(streq(v, v1), EXIT_FAILURE);
+
+ assert_return(freecount == 0, EXIT_FAILURE);
+
+ hash_free(h);
+ return 0;
+}
+DEFINE_TEST(test_hash_replace_failing,
+ .description = "test hash_add_unique failing to replace existing value");
+
+
+static int test_hash_iter(const struct test *t)
+{
+ struct hash *h = hash_new(8, NULL);
+ struct hash *h2 = hash_new(8, NULL);
+ const char *k1 = "k1", *k2 = "k2", *k3 = "k3";
+ const char *v1 = "v1", *v2 = "v2", *v3 = "v3";
+ struct hash_iter iter;
+ const char *k, *v;
+
+ hash_add(h, k1, v1);
+ hash_add(h2, k1, v1);
+ hash_add(h, k2, v2);
+ hash_add(h2, k2, v2);
+ hash_add(h, k3, v3);
+ hash_add(h2, k3, v3);
+
+ for (hash_iter_init(h, &iter);
+ hash_iter_next(&iter, &k, (const void **) &v);) {
+ v2 = hash_find(h2, k);
+ assert_return(v2 != NULL, EXIT_FAILURE);
+ hash_del(h2, k);
+ }
+
+ assert_return(hash_get_count(h) == 3, EXIT_FAILURE);
+ assert_return(hash_get_count(h2) == 0, EXIT_FAILURE);
+
+ hash_free(h);
+ hash_free(h2);
+ return 0;
+}
+DEFINE_TEST(test_hash_iter,
+ .description = "test hash_iter");
+
+
+static int test_hash_iter_after_del(const struct test *t)
+{
+ struct hash *h = hash_new(8, NULL);
+ struct hash *h2 = hash_new(8, NULL);
+ const char *k1 = "k1", *k2 = "k2", *k3 = "k3";
+ const char *v1 = "v1", *v2 = "v2", *v3 = "v3";
+ struct hash_iter iter;
+ const char *k, *v;
+
+ hash_add(h, k1, v1);
+ hash_add(h2, k1, v1);
+ hash_add(h, k2, v2);
+ hash_add(h2, k2, v2);
+ hash_add(h, k3, v3);
+ hash_add(h2, k3, v3);
+
+ hash_del(h, k1);
+
+ for (hash_iter_init(h, &iter);
+ hash_iter_next(&iter, &k, (const void **) &v);) {
+ v2 = hash_find(h2, k);
+ assert_return(v2 != NULL, EXIT_FAILURE);
+ hash_del(h2, k);
+ }
+
+ assert_return(hash_get_count(h) == 2, EXIT_FAILURE);
+ assert_return(hash_get_count(h2) == 1, EXIT_FAILURE);
+
+ hash_free(h);
+ hash_free(h2);
+ return 0;
+}
+DEFINE_TEST(test_hash_iter_after_del,
+ .description = "test hash_iter, after deleting element");
+
+
+static int test_hash_free(const struct test *t)
+{
+ struct hash *h = hash_new(8, countfreecalls);
+ const char *k1 = "k1", *k2 = "k2", *k3 = "k3";
+ const char *v1 = "v1", *v2 = "v2", *v3 = "v3";
+
+ hash_add(h, k1, v1);
+ hash_add(h, k2, v2);
+ hash_add(h, k3, v3);
+
+ hash_del(h, k1);
+
+ assert_return(freecount == 1, EXIT_FAILURE);
+
+ assert_return(hash_get_count(h) == 2, EXIT_FAILURE);
+
+ hash_free(h);
+
+ assert_return(freecount == 3, EXIT_FAILURE);
+
+ return 0;
+}
+DEFINE_TEST(test_hash_free,
+ .description = "test hash_free calling free function for all values");
+
+
+static int test_hash_add_unique(const struct test *t)
+{
+ const char *k[] = { "k1", "k2", "k3", "k4", "k5" };
+ const char *v[] = { "v1", "v2", "v3", "v4", "v5" };
+ unsigned int i, j, N;
+
+ N = ARRAY_SIZE(k);
+ for (i = 0; i < N; i++) {
+ /* With N - 1 buckets, there'll be a bucket with more than one key. */
+ struct hash *h = hash_new(N - 1, NULL);
+
+ /* Add the keys in different orders. */
+ for (j = 0; j < N; j++) {
+ unsigned int idx = (j + i) % N;
+ hash_add_unique(h, k[idx], v[idx]);
+ }
+
+ assert_return(hash_get_count(h) == N, EXIT_FAILURE);
+ hash_free(h);
+ }
+ return 0;
+}
+DEFINE_TEST(test_hash_add_unique,
+ .description = "test hash_add_unique with different key orders")
+
+
+static int test_hash_massive_add_del(const struct test *t)
+{
+ char buf[1024 * 8];
+ char *k;
+ struct hash *h;
+ unsigned int i, N = 1024;
+
+ h = hash_new(8, NULL);
+
+ k = &buf[0];
+ for (i = 0; i < N; i++) {
+ snprintf(k, 8, "k%d", i);
+ hash_add(h, k, k);
+ k += 8;
+ }
+
+ assert_return(hash_get_count(h) == N, EXIT_FAILURE);
+
+ k = &buf[0];
+ for (i = 0; i < N; i++) {
+ hash_del(h, k);
+ k += 8;
+ }
+
+ assert_return(hash_get_count(h) == 0, EXIT_FAILURE);
+
+ hash_free(h);
+ return 0;
+}
+DEFINE_TEST(test_hash_massive_add_del,
+ .description = "test multiple adds followed by multiple dels")
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-init.c b/testsuite/test-init.c
new file mode 100644
index 0000000..edbfc23
--- /dev/null
+++ b/testsuite/test-init.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <shared/macro.h>
+
+#include <libkmod/libkmod.h>
+
+#include "testsuite.h"
+
+static noreturn int test_load_resources(const struct test *t)
+{
+ struct kmod_ctx *ctx;
+ const char *null_config = NULL;
+ int err;
+
+ ctx = kmod_new(NULL, &null_config);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ kmod_set_log_priority(ctx, 7);
+
+ err = kmod_load_resources(ctx);
+ if (err != 0) {
+ ERR("could not load libkmod resources: %s\n", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+
+ kmod_unref(ctx);
+
+ exit(EXIT_SUCCESS);
+}
+DEFINE_TEST(test_load_resources,
+ .description = "test if kmod_load_resources works (recent modprobe on kernel without modules.builtin.modinfo)",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-init-load-resources/",
+ [TC_UNAME_R] = "5.6.0",
+ },
+ .need_spawn = true);
+
+DEFINE_TEST(test_load_resources,
+ .description = "test if kmod_load_resources works with empty modules.builtin.aliases.bin (recent depmod on kernel without modules.builtin.modinfo)",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-init-load-resources-empty-builtin-aliases-bin/",
+ [TC_UNAME_R] = "5.6.0",
+ },
+ .need_spawn = true);
+
+static noreturn int test_initlib(const struct test *t)
+{
+ struct kmod_ctx *ctx;
+ const char *null_config = NULL;
+
+ ctx = kmod_new(NULL, &null_config);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ kmod_unref(ctx);
+
+ exit(EXIT_SUCCESS);
+}
+DEFINE_TEST(test_initlib,
+ .description = "test if libkmod's init function work");
+
+static noreturn int test_insert(const struct test *t)
+{
+ struct kmod_ctx *ctx;
+ struct kmod_module *mod;
+ const char *null_config = NULL;
+ int err;
+
+ ctx = kmod_new(NULL, &null_config);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ err = kmod_module_new_from_path(ctx, "/mod-simple.ko", &mod);
+ if (err != 0) {
+ ERR("could not create module from path: %m\n");
+ exit(EXIT_FAILURE);
+ }
+
+ err = kmod_module_insert_module(mod, 0, NULL);
+ if (err != 0) {
+ ERR("could not insert module: %m\n");
+ exit(EXIT_FAILURE);
+ }
+ kmod_unref(ctx);
+
+ exit(EXIT_SUCCESS);
+}
+DEFINE_TEST(test_insert,
+ .description = "test if libkmod's insert_module returns ok",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-init/",
+ [TC_INIT_MODULE_RETCODES] = "bla:1:20",
+ },
+ .modules_loaded = "mod_simple",
+ .need_spawn = true);
+
+static noreturn int test_remove(const struct test *t)
+{
+ struct kmod_ctx *ctx;
+ struct kmod_module *mod_simple, *mod_bla;
+ const char *null_config = NULL;
+ int err;
+
+ ctx = kmod_new(NULL, &null_config);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ err = kmod_module_new_from_name(ctx, "mod-simple", &mod_simple);
+ if (err != 0) {
+ ERR("could not create module from name: %s\n", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+
+ err = kmod_module_new_from_name(ctx, "bla", &mod_bla);
+ if (err != 0) {
+ ERR("could not create module from name: %s\n", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+
+ err = kmod_module_remove_module(mod_simple, 0);
+ if (err != 0) {
+ ERR("could not remove module: %s\n", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+
+ err = kmod_module_remove_module(mod_bla, 0);
+ if (err != -ENOENT) {
+ ERR("wrong return code for failure test: %d\n", err);
+ exit(EXIT_FAILURE);
+ }
+
+ kmod_unref(ctx);
+
+ exit(EXIT_SUCCESS);
+}
+DEFINE_TEST(test_remove,
+ .description = "test if libkmod's remove_module returns ok",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-remove/",
+ [TC_DELETE_MODULE_RETCODES] =
+ "mod-simple:0:0:bla:-1:" STRINGIFY(ENOENT),
+ },
+ .need_spawn = true);
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-initstate.c b/testsuite/test-initstate.c
new file mode 100644
index 0000000..9332e8f
--- /dev/null
+++ b/testsuite/test-initstate.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <libkmod/libkmod.h>
+
+#include <shared/macro.h>
+
+#include "testsuite.h"
+
+
+static noreturn int test_initstate_from_lookup(const struct test *t)
+{
+ struct kmod_ctx *ctx;
+ struct kmod_list *list = NULL;
+ struct kmod_module *mod;
+ const char *null_config = NULL;
+ int err, r;
+
+ ctx = kmod_new(NULL, &null_config);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ err = kmod_module_new_from_lookup(ctx, "fake-builtin", &list);
+ if (err < 0) {
+ ERR("could not create module from lookup: %s\n", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+
+ if (!list) {
+ ERR("could not create module from lookup: module not found: fake-builtin\n");
+ exit(EXIT_FAILURE);
+ }
+
+ mod = kmod_module_get_module(list);
+
+ r = kmod_module_get_initstate(mod);
+ if (r != KMOD_MODULE_BUILTIN) {
+ ERR("module should have builtin state but is: %s\n",
+ kmod_module_initstate_str(r));
+ exit(EXIT_FAILURE);
+ }
+
+ kmod_module_unref(mod);
+ kmod_module_unref_list(list);
+ kmod_unref(ctx);
+
+ exit(EXIT_SUCCESS);
+}
+DEFINE_TEST(test_initstate_from_lookup,
+ .description = "test if libkmod return correct initstate for builtin module from lookup",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-initstate",
+ [TC_UNAME_R] = "4.4.4",
+ },
+ .need_spawn = true);
+
+static noreturn int test_initstate_from_name(const struct test *t)
+{
+ struct kmod_ctx *ctx;
+ struct kmod_module *mod = NULL;
+ const char *null_config = NULL;
+ int err, r;
+
+ ctx = kmod_new(NULL, &null_config);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ err = kmod_module_new_from_name(ctx, "fake-builtin", &mod);
+ if (err != 0) {
+ ERR("could not create module from lookup: %s\n", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+
+ if (!mod) {
+ ERR("could not create module from lookup: module not found: fake-builtin\n");
+ exit(EXIT_FAILURE);
+ }
+
+ r = kmod_module_get_initstate(mod);
+ if (r != KMOD_MODULE_BUILTIN) {
+ ERR("module should have builtin state but is: %s\n",
+ kmod_module_initstate_str(r));
+ exit(EXIT_FAILURE);
+ }
+
+ kmod_module_unref(mod);
+ kmod_unref(ctx);
+
+ exit(EXIT_SUCCESS);
+}
+DEFINE_TEST(test_initstate_from_name,
+ .description = "test if libkmod return correct initstate for builtin module from name",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-initstate",
+ [TC_UNAME_R] = "4.4.4",
+ },
+ .need_spawn = true);
+
+
+
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-list.c b/testsuite/test-list.c
new file mode 100644
index 0000000..535e8d7
--- /dev/null
+++ b/testsuite/test-list.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libkmod/libkmod-internal.h>
+
+/* FIXME: hack, change name so we don't clash */
+#undef ERR
+#include "testsuite.h"
+
+static int len(struct kmod_list *list)
+{
+ int count = 0;
+ struct kmod_list *l;
+ kmod_list_foreach(l, list)
+ count++;
+ return count;
+}
+
+
+static int test_list_last(const struct test *t)
+{
+ struct kmod_list *list = NULL, *last;
+ int i;
+ const char *v[] = { "v1", "v2", "v3", "v4", "v5" };
+ const int N = ARRAY_SIZE(v);
+
+ for (i = 0; i < N; i++)
+ list = kmod_list_append(list, v[i]);
+ assert_return(len(list) == N, EXIT_FAILURE);
+
+ last = kmod_list_last(list);
+ assert_return(last->data == v[N - 1], EXIT_FAILURE);
+
+ return 0;
+}
+DEFINE_TEST(test_list_last,
+ .description = "test for the last element of a list");
+
+
+static int test_list_prev(const struct test *t)
+{
+ struct kmod_list *list = NULL, *l, *p;
+ int i;
+ const char *v[] = { "v1", "v2", "v3", "v4", "v5" };
+ const int N = ARRAY_SIZE(v);
+
+ l = kmod_list_prev(list, list);
+ assert_return(l == NULL, EXIT_FAILURE);
+
+ for (i = 0; i < N; i++)
+ list = kmod_list_append(list, v[i]);
+
+ l = kmod_list_prev(list, list);
+ assert_return(l == NULL, EXIT_FAILURE);
+
+ l = list;
+ for (i = 0; i < N - 1; i++) {
+ l = kmod_list_next(list, l);
+ p = kmod_list_prev(list, l);
+ assert_return(p->data == v[i], EXIT_FAILURE);
+ }
+
+ return 0;
+}
+DEFINE_TEST(test_list_prev,
+ .description = "test list prev");
+
+
+static int test_list_remove_n_latest(const struct test *t)
+{
+ struct kmod_list *list = NULL, *l;
+ int i;
+ const char *v[] = { "v1", "v2", "v3", "v4", "v5" };
+ const int N = ARRAY_SIZE(v), M = N / 2;
+
+ for (i = 0; i < N; i++)
+ list = kmod_list_append(list, v[i]);
+ assert_return(len(list) == N, EXIT_FAILURE);
+
+ list = kmod_list_remove_n_latest(list, M);
+ assert_return(len(list) == N - M, EXIT_FAILURE);
+
+ i = 0;
+ kmod_list_foreach (l, list) {
+ assert_return(l->data == v[i], EXIT_FAILURE);
+ i++;
+ }
+
+ return 0;
+}
+DEFINE_TEST(test_list_remove_n_latest,
+ .description = "test list function to remove n latest elements");
+
+
+static int test_list_remove_data(const struct test *t)
+{
+ struct kmod_list *list = NULL, *l;
+ int i;
+ const char *v[] = { "v1", "v2", "v3", "v4", "v5" }, *removed;
+ const int N = ARRAY_SIZE(v);
+
+ for (i = 0; i < N; i++)
+ list = kmod_list_append(list, v[i]);
+
+ removed = v[N / 2];
+ list = kmod_list_remove_data(list, removed);
+ assert_return(len(list) == N - 1, EXIT_FAILURE);
+
+ kmod_list_foreach (l, list)
+ assert_return(l->data != removed, EXIT_FAILURE);
+
+ return 0;
+}
+DEFINE_TEST(test_list_remove_data,
+ .description = "test list function to remove element by data");
+
+
+static int test_list_append_list(const struct test *t)
+{
+ struct kmod_list *a = NULL, *b = NULL, *c, *l;
+ int i;
+ const char *v[] = { "v1", "v2", "v3", "v4", "v5" };
+ const int N = ARRAY_SIZE(v), M = N / 2;
+
+ for (i = 0; i < M; i++)
+ a = kmod_list_append(a, v[i]);
+ assert_return(len(a) == M, EXIT_FAILURE);
+
+ for (i = M; i < N; i++)
+ b = kmod_list_append(b, v[i]);
+ assert_return(len(b) == N - M, EXIT_FAILURE);
+
+ a = kmod_list_append_list(a, NULL);
+ assert_return(len(a) == M, EXIT_FAILURE);
+
+ b = kmod_list_append_list(NULL, b);
+ assert_return(len(b) == N - M, EXIT_FAILURE);
+
+ c = kmod_list_append_list(a, b);
+ assert_return(len(c) == N, EXIT_FAILURE);
+
+ i = 0;
+ kmod_list_foreach (l, c) {
+ assert_return(l->data == v[i], EXIT_FAILURE);
+ i++;
+ }
+
+ return 0;
+}
+DEFINE_TEST(test_list_append_list,
+ .description = "test list function to append another list");
+
+
+static int test_list_insert_before(const struct test *t)
+{
+ struct kmod_list *list = NULL, *l;
+ const char *v1 = "v1", *v2 = "v2", *v3 = "v3", *vx = "vx";
+
+ list = kmod_list_insert_before(list, v3);
+ assert_return(len(list) == 1, EXIT_FAILURE);
+
+ list = kmod_list_insert_before(list, v2);
+ list = kmod_list_insert_before(list, v1);
+ assert_return(len(list) == 3, EXIT_FAILURE);
+
+ l = list;
+ assert_return(l->data == v1, EXIT_FAILURE);
+
+ l = kmod_list_next(list, l);
+ assert_return(l->data == v2, EXIT_FAILURE);
+
+ l = kmod_list_insert_before(l, vx);
+ assert_return(len(list) == 4, EXIT_FAILURE);
+ assert_return(l->data == vx, EXIT_FAILURE);
+
+ l = kmod_list_next(list, l);
+ assert_return(l->data == v2, EXIT_FAILURE);
+
+ l = kmod_list_next(list, l);
+ assert_return(l->data == v3, EXIT_FAILURE);
+
+ return 0;
+}
+DEFINE_TEST(test_list_insert_before,
+ .description = "test list function to insert before element");
+
+
+static int test_list_insert_after(const struct test *t)
+{
+ struct kmod_list *list = NULL, *l;
+ const char *v1 = "v1", *v2 = "v2", *v3 = "v3", *vx = "vx";
+
+ list = kmod_list_insert_after(list, v1);
+ assert_return(len(list) == 1, EXIT_FAILURE);
+
+ list = kmod_list_insert_after(list, v3);
+ list = kmod_list_insert_after(list, v2);
+ assert_return(len(list) == 3, EXIT_FAILURE);
+
+ l = list;
+ assert_return(l->data == v1, EXIT_FAILURE);
+
+ l = kmod_list_insert_after(l, vx);
+ assert_return(len(list) == 4, EXIT_FAILURE);
+ assert_return(l->data == v1, EXIT_FAILURE);
+
+ l = kmod_list_next(list, l);
+ assert_return(l->data == vx, EXIT_FAILURE);
+
+ l = kmod_list_next(list, l);
+ assert_return(l->data == v2, EXIT_FAILURE);
+
+ l = kmod_list_next(list, l);
+ assert_return(l->data == v3, EXIT_FAILURE);
+
+ return 0;
+}
+DEFINE_TEST(test_list_insert_after,
+ .description = "test list function to insert after element");
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-loaded.c b/testsuite/test-loaded.c
new file mode 100644
index 0000000..9d5814f
--- /dev/null
+++ b/testsuite/test-loaded.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libkmod/libkmod.h>
+
+#include "testsuite.h"
+
+static int loaded_1(const struct test *t)
+{
+ struct kmod_ctx *ctx;
+ const char *null_config = NULL;
+ struct kmod_list *list, *itr;
+ int err;
+
+ ctx = kmod_new(NULL, &null_config);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ err = kmod_module_new_from_loaded(ctx, &list);
+ if (err < 0) {
+ fprintf(stderr, "%s\n", strerror(-err));
+ kmod_unref(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Module Size Used by\n");
+
+ kmod_list_foreach(itr, list) {
+ struct kmod_module *mod = kmod_module_get_module(itr);
+ const char *name = kmod_module_get_name(mod);
+ int use_count = kmod_module_get_refcnt(mod);
+ long size = kmod_module_get_size(mod);
+ struct kmod_list *holders, *hitr;
+ int first = 1;
+
+ printf("%-19s %8ld %d ", name, size, use_count);
+ holders = kmod_module_get_holders(mod);
+ kmod_list_foreach(hitr, holders) {
+ struct kmod_module *hm = kmod_module_get_module(hitr);
+
+ if (!first)
+ putchar(',');
+ else
+ first = 0;
+
+ fputs(kmod_module_get_name(hm), stdout);
+ kmod_module_unref(hm);
+ }
+ putchar('\n');
+ kmod_module_unref_list(holders);
+ kmod_module_unref(mod);
+ }
+ kmod_module_unref_list(list);
+
+ kmod_unref(ctx);
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(loaded_1,
+ .description = "check if list of module is created",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-loaded/",
+ },
+ .need_spawn = true,
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-loaded/correct.txt",
+ });
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-modinfo.c b/testsuite/test-modinfo.c
new file mode 100644
index 0000000..373dc95
--- /dev/null
+++ b/testsuite/test-modinfo.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "testsuite.h"
+
+static const char *progname = ABS_TOP_BUILDDIR "/tools/modinfo";
+
+#define DEFINE_MODINFO_TEST(_field, _flavor, ...) \
+static noreturn int test_modinfo_##_field(const struct test *t) \
+{ \
+ const char *const args[] = { \
+ progname, "-F", #_field ,\
+ __VA_ARGS__ , \
+ NULL, \
+ }; \
+ test_spawn_prog(progname, args); \
+ exit(EXIT_FAILURE); \
+} \
+DEFINE_TEST(test_modinfo_##_field, \
+ .description = "check " #_field " output of modinfo for different architectures", \
+ .config = { \
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modinfo/", \
+ }, \
+ .output = { \
+ .out = TESTSUITE_ROOTFS "test-modinfo/correct-" #_field #_flavor ".txt", \
+ })
+
+#define DEFINE_MODINFO_GENERIC_TEST(_field) \
+ DEFINE_MODINFO_TEST(_field, , \
+ "/mod-simple-i386.ko", \
+ "/mod-simple-x86_64.ko", \
+ "/mod-simple-sparc64.ko")
+
+#ifdef ENABLE_OPENSSL
+#define DEFINE_MODINFO_SIGN_TEST(_field) \
+ DEFINE_MODINFO_TEST(_field, -openssl, \
+ "/mod-simple-sha1.ko", \
+ "/mod-simple-sha256.ko", \
+ "/mod-simple-pkcs7.ko")
+#else
+#define DEFINE_MODINFO_SIGN_TEST(_field) \
+ DEFINE_MODINFO_TEST(_field, , \
+ "/mod-simple-sha1.ko", \
+ "/mod-simple-sha256.ko", \
+ "/mod-simple-pkcs7.ko")
+#endif
+
+DEFINE_MODINFO_GENERIC_TEST(filename);
+DEFINE_MODINFO_GENERIC_TEST(author);
+DEFINE_MODINFO_GENERIC_TEST(license);
+DEFINE_MODINFO_GENERIC_TEST(description);
+DEFINE_MODINFO_GENERIC_TEST(parm);
+DEFINE_MODINFO_GENERIC_TEST(depends);
+
+DEFINE_MODINFO_SIGN_TEST(signer);
+DEFINE_MODINFO_SIGN_TEST(sig_key);
+DEFINE_MODINFO_SIGN_TEST(sig_hashalgo);
+
+#if 0
+static noreturn int test_modinfo_signature(const struct test *t)
+{
+ const char *const args[] = {
+ progname,
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(test_modinfo_signature,
+ .description = "check signatures are correct for modinfo is correct for i686, ppc64, s390x and x86_64",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modinfo/",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modinfo/correct.txt",
+ });
+#endif
+
+static noreturn int test_modinfo_external(const struct test *t)
+{
+ const char *const args[] = {
+ progname, "-F", "filename",
+ "mod-simple",
+ NULL,
+ };
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(test_modinfo_external,
+ .description = "check if modinfo finds external module",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modinfo/external",
+ [TC_UNAME_R] = "4.4.4",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modinfo/correct-external.txt",
+ })
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-modprobe.c b/testsuite/test-modprobe.c
new file mode 100644
index 0000000..309f3e3
--- /dev/null
+++ b/testsuite/test-modprobe.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "testsuite.h"
+
+static noreturn int modprobe_show_depends(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "--show-depends", "mod-loop-a",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_show_depends,
+ .description = "check if output for modprobe --show-depends is correct for loaded modules",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/show-depends",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/show-depends/correct.txt",
+ });
+
+static noreturn int modprobe_show_depends2(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "--show-depends", "mod-simple",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_show_depends2,
+ .description = "check if output for modprobe --show-depends is correct",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/show-depends",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/show-depends/correct-mod-simple.txt",
+ });
+
+
+static noreturn int modprobe_show_alias_to_none(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "--show-depends", "--ignore-install", "--quiet", "mod-simple",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_show_alias_to_none,
+ .description = "check if modprobe --show-depends doesn't explode with an alias to nothing",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/alias-to-none",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/alias-to-none/correct.txt",
+ },
+ .modules_loaded = "",
+ );
+
+
+static noreturn int modprobe_show_exports(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "--show-exports", "--quiet", "/mod-loop-a.ko",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_show_exports,
+ .description = "check if modprobe --show-depends doesn't explode with an alias to nothing",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/show-exports",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/show-exports/correct.txt",
+ .regex = true,
+ });
+
+
+static noreturn int modprobe_builtin(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "unix",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_builtin,
+ .description = "check if modprobe return 0 for builtin",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/builtin",
+ });
+
+static noreturn int modprobe_builtin_lookup_only(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "-R", "unix",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_builtin_lookup_only,
+ .description = "check if modprobe -R correctly returns the builtin module",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/builtin",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/builtin/correct.txt",
+ });
+
+static noreturn int modprobe_softdep_loop(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "mod-loop-b",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_softdep_loop,
+ .description = "check if modprobe breaks softdep loop",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/softdep-loop",
+ [TC_INIT_MODULE_RETCODES] = "",
+ },
+ .modules_loaded = "mod-loop-a,mod-loop-b",
+ );
+
+static noreturn int modprobe_install_cmd_loop(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "mod-loop-a",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_install_cmd_loop,
+ .description = "check if modprobe breaks install-commands loop",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/install-cmd-loop",
+ [TC_INIT_MODULE_RETCODES] = "",
+ },
+ .env_vars = (const struct keyval[]) {
+ { "MODPROBE", ABS_TOP_BUILDDIR "/tools/modprobe" },
+ { }
+ },
+ .modules_loaded = "mod-loop-b,mod-loop-a",
+ );
+
+static noreturn int modprobe_param_kcmdline_show_deps(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "--show-depends", "mod-simple",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_param_kcmdline_show_deps,
+ .description = "check if params from kcmdline are passed to (f)init_module call",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline/correct.txt",
+ },
+ .modules_loaded = "",
+ );
+
+static noreturn int modprobe_param_kcmdline(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "-c",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline2, modprobe_param_kcmdline,
+ .description = "check if params with no value are parsed correctly from kcmdline",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline2",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline2/correct.txt",
+ },
+ .modules_loaded = "",
+ );
+
+DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline3, modprobe_param_kcmdline,
+ .description = "check if unrelated strings in kcmdline are correctly ignored",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline3",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline3/correct.txt",
+ },
+ .modules_loaded = "",
+ );
+
+DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline4, modprobe_param_kcmdline,
+ .description = "check if unrelated strings in kcmdline are correctly ignored",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline4",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline4/correct.txt",
+ },
+ .modules_loaded = "",
+ );
+
+DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline5, modprobe_param_kcmdline,
+ .description = "check if params with spaces are parsed correctly from kcmdline",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline5",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline5/correct.txt",
+ },
+ .modules_loaded = "",
+ );
+
+DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline6, modprobe_param_kcmdline,
+ .description = "check if dots on other parts of kcmdline don't confuse our parser",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline6",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline6/correct.txt",
+ },
+ .modules_loaded = "",
+ );
+
+DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline7, modprobe_param_kcmdline,
+ .description = "check if dots on other parts of kcmdline don't confuse our parser",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline7",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline7/correct.txt",
+ },
+ .modules_loaded = "",
+ );
+
+DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline8, modprobe_param_kcmdline,
+ .description = "check if dots on other parts of kcmdline don't confuse our parser",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline8",
+ },
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline8/correct.txt",
+ },
+ .modules_loaded = "",
+ );
+
+
+static noreturn int modprobe_force(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "--force", "mod-simple",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_force,
+ .description = "check modprobe --force",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/force",
+ [TC_INIT_MODULE_RETCODES] = "",
+ },
+ .modules_loaded = "mod-simple",
+ );
+
+static noreturn int modprobe_oldkernel(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "mod-simple",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_oldkernel,
+ .description = "check modprobe with kernel without finit_module()",
+ .config = {
+ [TC_UNAME_R] = "3.3.3",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/oldkernel",
+ [TC_INIT_MODULE_RETCODES] = "",
+ },
+ .modules_loaded = "mod-simple",
+ );
+
+static noreturn int modprobe_oldkernel_force(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "--force", "mod-simple",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_oldkernel_force,
+ .description = "check modprobe --force with kernel without finit_module()",
+ .config = {
+ [TC_UNAME_R] = "3.3.3",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/oldkernel-force",
+ [TC_INIT_MODULE_RETCODES] = "",
+ },
+ .modules_loaded = "mod-simple",
+ );
+
+static noreturn int modprobe_external(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "mod-simple",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_external,
+ .description = "check modprobe able to load external module",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/external",
+ [TC_INIT_MODULE_RETCODES] = "",
+ },
+ .modules_loaded = "mod-simple",
+ );
+
+static noreturn int modprobe_module_from_abspath(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "/home/foo/mod-simple.ko",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_module_from_abspath,
+ .description = "check modprobe able to load module given as an absolute path",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-from-abspath",
+ [TC_INIT_MODULE_RETCODES] = "",
+ },
+ .modules_loaded = "mod-simple",
+ );
+
+static noreturn int modprobe_module_from_relpath(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe";
+ const char *const args[] = {
+ progname,
+ "./mod-simple.ko",
+ NULL,
+ };
+
+ if (chdir("/home/foo") != 0) {
+ perror("failed to change into /home/foo");
+ exit(EXIT_FAILURE);
+ }
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(modprobe_module_from_relpath,
+ .description = "check modprobe able to load module given as a relative path",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-from-relpath",
+ [TC_INIT_MODULE_RETCODES] = "",
+ },
+ .need_spawn = true,
+ .modules_loaded = "mod-simple",
+ );
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-new-module.c b/testsuite/test-new-module.c
new file mode 100644
index 0000000..9872b78
--- /dev/null
+++ b/testsuite/test-new-module.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libkmod/libkmod.h>
+
+#include "testsuite.h"
+
+static int from_name(const struct test *t)
+{
+ static const char *const modnames[] = {
+ "ext4",
+ "balbalbalbbalbalbalbalbalbalbal",
+ "snd-hda-intel",
+ "snd-timer",
+ "iTCO_wdt",
+ NULL,
+ };
+ const char *const *p;
+ struct kmod_ctx *ctx;
+ struct kmod_module *mod;
+ const char *null_config = NULL;
+ int err;
+
+ ctx = kmod_new(NULL, &null_config);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ for (p = modnames; p != NULL; p++) {
+ err = kmod_module_new_from_name(ctx, *p, &mod);
+ if (err < 0)
+ exit(EXIT_SUCCESS);
+
+ printf("modname: %s\n", kmod_module_get_name(mod));
+ kmod_module_unref(mod);
+ }
+
+ kmod_unref(ctx);
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(from_name,
+ .description = "check if module names are parsed correctly",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-new-module/from_name/",
+ },
+ .need_spawn = true,
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-new-module/from_name/correct.txt",
+ });
+
+static int from_alias(const struct test *t)
+{
+ static const char *const modnames[] = {
+ "ext4.*",
+ NULL,
+ };
+ const char *const *p;
+ struct kmod_ctx *ctx;
+ int err;
+
+ ctx = kmod_new(NULL, NULL);
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ for (p = modnames; p != NULL; p++) {
+ struct kmod_list *l, *list = NULL;
+
+ err = kmod_module_new_from_lookup(ctx, *p, &list);
+ if (err < 0)
+ exit(EXIT_SUCCESS);
+
+ kmod_list_foreach(l, list) {
+ struct kmod_module *m;
+ m = kmod_module_get_module(l);
+
+ printf("modname: %s\n", kmod_module_get_name(m));
+ kmod_module_unref(m);
+ }
+ kmod_module_unref_list(list);
+ }
+
+ kmod_unref(ctx);
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(from_alias,
+ .description = "check if aliases are parsed correctly",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-new-module/from_alias/",
+ },
+ .need_spawn = true,
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-new-module/from_alias/correct.txt",
+ });
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-scratchbuf.c b/testsuite/test-scratchbuf.c
new file mode 100644
index 0000000..6d86957
--- /dev/null
+++ b/testsuite/test-scratchbuf.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <shared/scratchbuf.h>
+
+#include "testsuite.h"
+
+static int test_scratchbuf_onlystack(const struct test *t)
+{
+ struct scratchbuf sbuf;
+ const char *smallstr = "xyz";
+ char buf[3 + 2];
+ char buf2[3 + 1];
+
+ scratchbuf_init(&sbuf, buf, sizeof(buf));
+ assert_return(scratchbuf_alloc(&sbuf, strlen(smallstr) + 1) == 0, EXIT_FAILURE);
+ assert_return(sbuf.need_free == false, EXIT_FAILURE);
+ scratchbuf_release(&sbuf);
+
+ scratchbuf_init(&sbuf, buf2, sizeof(buf2));
+ assert_return(scratchbuf_alloc(&sbuf, strlen(smallstr) + 1) == 0, EXIT_FAILURE);
+ assert_return(sbuf.need_free == false, EXIT_FAILURE);
+ scratchbuf_release(&sbuf);
+
+ memcpy(scratchbuf_str(&sbuf), smallstr, strlen(smallstr) + 1);
+ assert_return(strcmp(scratchbuf_str(&sbuf), smallstr) == 0, EXIT_FAILURE);
+
+ return 0;
+}
+DEFINE_TEST(test_scratchbuf_onlystack,
+ .description = "test scratchbuf for buffer on stack only");
+
+
+static int test_scratchbuf_heap(const struct test *t)
+{
+ struct scratchbuf sbuf;
+ const char *smallstr = "xyz";
+ const char *largestr = "xyzxyzxyz";
+ const char *largestr2 = "xyzxyzxyzxyzxyz";
+ char buf[3 + 1];
+
+ scratchbuf_init(&sbuf, buf, sizeof(buf));
+
+ /* Initially only on stack */
+ assert_return(scratchbuf_alloc(&sbuf, strlen(smallstr) + 1) == 0, EXIT_FAILURE);
+ assert_return(sbuf.need_free == false, EXIT_FAILURE);
+ memcpy(scratchbuf_str(&sbuf), smallstr, strlen(smallstr) + 1);
+
+ /* Grow once to heap */
+ assert_return(scratchbuf_alloc(&sbuf, strlen(largestr) + 1) == 0, EXIT_FAILURE);
+ assert_return(sbuf.need_free == true, EXIT_FAILURE);
+ assert_return(sbuf.size == strlen(largestr) + 1, EXIT_FAILURE);
+ assert_return(strcmp(scratchbuf_str(&sbuf), smallstr) == 0, EXIT_FAILURE);
+ memcpy(scratchbuf_str(&sbuf), largestr, strlen(largestr) + 1);
+ assert_return(strcmp(scratchbuf_str(&sbuf), largestr) == 0, EXIT_FAILURE);
+
+ /* Grow again - realloc should take place */
+ assert_return(scratchbuf_alloc(&sbuf, strlen(largestr2) + 1) == 0, EXIT_FAILURE);
+ assert_return(sbuf.need_free == true, EXIT_FAILURE);
+ assert_return(sbuf.size == strlen(largestr2) + 1, EXIT_FAILURE);
+ memcpy(scratchbuf_str(&sbuf), largestr2, strlen(largestr2) + 1);
+ assert_return(strcmp(scratchbuf_str(&sbuf), largestr2) == 0, EXIT_FAILURE);
+
+ scratchbuf_release(&sbuf);
+
+ return 0;
+}
+DEFINE_TEST(test_scratchbuf_heap,
+ .description = "test scratchbuf for buffer on that grows to heap");
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-strbuf.c b/testsuite/test-strbuf.c
new file mode 100644
index 0000000..c98c91d
--- /dev/null
+++ b/testsuite/test-strbuf.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <shared/strbuf.h>
+#include <shared/util.h>
+
+#include "testsuite.h"
+
+static const char *TEXT =
+ "this is a very long test that is longer than the size we initially se in the strbuf";
+
+static int test_strbuf_pushchar(const struct test *t)
+{
+ struct strbuf buf;
+ char *result1, *result2;
+ const char *c;
+
+ strbuf_init(&buf);
+
+ for (c = TEXT; *c != '\0'; c++)
+ strbuf_pushchar(&buf, *c);
+
+ result1 = (char *) strbuf_str(&buf);
+ assert_return(result1 == buf.bytes, EXIT_FAILURE);
+ assert_return(streq(result1, TEXT), EXIT_FAILURE);
+ result1 = strdup(result1);
+
+ result2 = strbuf_steal(&buf);
+ assert_return(streq(result1, result2), EXIT_FAILURE);
+
+ free(result1);
+ free(result2);
+
+ return 0;
+}
+DEFINE_TEST(test_strbuf_pushchar,
+ .description = "test strbuf_{pushchar, str, steal}");
+
+static int test_strbuf_pushchars(const struct test *t)
+{
+ struct strbuf buf;
+ char *result1, *saveptr = NULL, *str, *result2;
+ const char *c;
+ int lastwordlen = 0;
+
+ strbuf_init(&buf);
+ str = strdup(TEXT);
+ for (c = strtok_r(str, " ", &saveptr); c != NULL;
+ c = strtok_r(NULL, " ", &saveptr)) {
+ strbuf_pushchars(&buf, c);
+ strbuf_pushchar(&buf, ' ');
+ lastwordlen = strlen(c);
+ }
+
+ strbuf_popchar(&buf);
+ result1 = (char *) strbuf_str(&buf);
+ assert_return(result1 == buf.bytes, EXIT_FAILURE);
+ assert_return(streq(result1, TEXT), EXIT_FAILURE);
+
+ strbuf_popchars(&buf, lastwordlen);
+ result2 = strbuf_steal(&buf);
+ assert_return(!streq(TEXT, result2), EXIT_FAILURE);
+ assert_return(strncmp(TEXT, result2, strlen(TEXT) - lastwordlen) == 0,
+ EXIT_FAILURE);
+ assert_return(result2[strlen(TEXT) - lastwordlen] == '\0',
+ EXIT_FAILURE);
+
+ free(str);
+ free(result2);
+
+ return 0;
+}
+DEFINE_TEST(test_strbuf_pushchars,
+ .description = "test strbuf_{pushchars, popchar, popchars}");
+
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-testsuite.c b/testsuite/test-testsuite.c
new file mode 100644
index 0000000..c77c4bb
--- /dev/null
+++ b/testsuite/test-testsuite.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#include <shared/util.h>
+
+#include <libkmod/libkmod.h>
+
+#include "testsuite.h"
+
+
+#define TEST_UNAME "4.0.20-kmod"
+static noreturn int testsuite_uname(const struct test *t)
+{
+ struct utsname u;
+ int err = uname(&u);
+
+ if (err < 0)
+ exit(EXIT_FAILURE);
+
+ if (!streq(u.release, TEST_UNAME)) {
+ char *ldpreload = getenv("LD_PRELOAD");
+ ERR("u.release=%s should be %s\n", u.release, TEST_UNAME);
+ ERR("LD_PRELOAD=%s\n", ldpreload);
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+}
+DEFINE_TEST(testsuite_uname,
+ .description = "test if trap to uname() works",
+ .config = {
+ [TC_UNAME_R] = TEST_UNAME,
+ },
+ .need_spawn = true);
+
+static int testsuite_rootfs_fopen(const struct test *t)
+{
+ FILE *fp;
+ char s[100];
+ int n;
+
+ fp = fopen(MODULE_DIRECTORY "/a", "r");
+ if (fp == NULL)
+ return EXIT_FAILURE;;
+
+ n = fscanf(fp, "%s", s);
+ if (n != 1)
+ return EXIT_FAILURE;
+
+ if (!streq(s, "kmod-test-chroot-works"))
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(testsuite_rootfs_fopen,
+ .description = "test if rootfs works - fopen()",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-rootfs/",
+ },
+ .need_spawn = true);
+
+static int testsuite_rootfs_open(const struct test *t)
+{
+ char buf[100];
+ int fd, done;
+
+ fd = open(MODULE_DIRECTORY "/a", O_RDONLY);
+ if (fd < 0)
+ return EXIT_FAILURE;
+
+ for (done = 0;;) {
+ int r = read(fd, buf + done, sizeof(buf) - 1 - done);
+ if (r == 0)
+ break;
+ if (r == -EAGAIN)
+ continue;
+
+ done += r;
+ }
+
+ buf[done] = '\0';
+
+ if (!streq(buf, "kmod-test-chroot-works\n"))
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(testsuite_rootfs_open,
+ .description = "test if rootfs works - open()",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-rootfs/",
+ },
+ .need_spawn = true);
+
+static int testsuite_rootfs_stat_access(const struct test *t)
+{
+ struct stat st;
+
+ if (access(MODULE_DIRECTORY "/a", F_OK) < 0) {
+ ERR("access failed: %m\n");
+ return EXIT_FAILURE;
+ }
+
+ if (stat(MODULE_DIRECTORY "/a", &st) < 0) {
+ ERR("stat failed: %m\n");
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(testsuite_rootfs_stat_access,
+ .description = "test if rootfs works - stat() and access()",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-rootfs/",
+ },
+ .need_spawn = true);
+
+static int testsuite_rootfs_opendir(const struct test *t)
+{
+ DIR *d;
+
+ d = opendir("/testdir");
+ if (d == NULL) {
+ ERR("opendir failed: %m\n");
+ return EXIT_FAILURE;
+ }
+
+ closedir(d);
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(testsuite_rootfs_opendir,
+ .description = "test if rootfs works - opendir()",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-rootfs/",
+ },
+ .need_spawn = true);
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-tools.c b/testsuite/test-tools.c
new file mode 100644
index 0000000..4a9ee9b
--- /dev/null
+++ b/testsuite/test-tools.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "testsuite.h"
+
+static noreturn int kmod_tool_insert(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/kmod";
+ const char *const args[] = {
+ progname,
+ "insert", "mod-simple",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(kmod_tool_insert,
+ .description = "check kmod insert",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-tools/insert",
+ [TC_INIT_MODULE_RETCODES] = "",
+ },
+ .modules_loaded = "mod-simple",
+ );
+
+static noreturn int kmod_tool_remove(const struct test *t)
+{
+ const char *progname = ABS_TOP_BUILDDIR "/tools/kmod";
+ const char *const args[] = {
+ progname,
+ "remove", "mod-simple",
+ NULL,
+ };
+
+ test_spawn_prog(progname, args);
+ exit(EXIT_FAILURE);
+}
+DEFINE_TEST(kmod_tool_remove,
+ .description = "check kmod remove",
+ .config = {
+ [TC_UNAME_R] = "4.4.4",
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-tools/remove",
+ [TC_DELETE_MODULE_RETCODES] = "",
+ },
+ );
+
+TESTSUITE_MAIN();
diff --git a/testsuite/test-util.c b/testsuite/test-util.c
new file mode 100644
index 0000000..e3243e8
--- /dev/null
+++ b/testsuite/test-util.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ * Copyright (C) 2012 Pedro Pedruzzi
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <shared/util.h>
+
+#include "testsuite.h"
+
+static int alias_1(const struct test *t)
+{
+ static const char *const input[] = {
+ "test1234",
+ "test[abcfoobar]2211",
+ "bar[aaa][bbbb]sss",
+ "kmod[p.b]lib",
+ "[az]1234[AZ]",
+ NULL,
+ };
+
+ char buf[PATH_MAX];
+ size_t len;
+ const char *const *alias;
+
+ for (alias = input; *alias != NULL; alias++) {
+ int ret;
+
+ ret = alias_normalize(*alias, buf, &len);
+ printf("input %s\n", *alias);
+ printf("return %d\n", ret);
+
+ if (ret == 0) {
+ printf("len %zu\n", len);
+ printf("output %s\n", buf);
+ }
+
+ printf("\n");
+ }
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(alias_1,
+ .description = "check if alias_normalize does the right thing",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-util/",
+ },
+ .need_spawn = true,
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-util/alias-correct.txt",
+ });
+
+static int test_freadline_wrapped(const struct test *t)
+{
+ FILE *fp = fopen("/freadline_wrapped-input.txt", "re");
+
+ if (!fp)
+ return EXIT_FAILURE;
+
+ while (!feof(fp) && !ferror(fp)) {
+ unsigned int num = 0;
+ char *s = freadline_wrapped(fp, &num);
+ if (!s)
+ break;
+ puts(s);
+ free(s);
+ printf("%u\n", num);
+ }
+
+ fclose(fp);
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_freadline_wrapped,
+ .description = "check if freadline_wrapped() does the right thing",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-util/",
+ },
+ .need_spawn = true,
+ .output = {
+ .out = TESTSUITE_ROOTFS "test-util/freadline_wrapped-correct.txt",
+ });
+
+static int test_strchr_replace(const struct test *t)
+{
+ _cleanup_free_ char *s = strdup("this is a test string");
+ const char *res = "thiC iC a teCt Ctring";
+
+ strchr_replace(s, 's', 'C');
+ assert_return(streq(s, res), EXIT_FAILURE);
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_strchr_replace,
+ .description = "check implementation of strchr_replace()",
+ .need_spawn = false,
+ );
+
+static int test_underscores(const struct test *t)
+{
+ struct teststr {
+ char *val;
+ const char *res;
+ } teststr[] = {
+ { strdup("aa-bb-cc_"), "aa_bb_cc_" },
+ { strdup("-aa-bb-cc-"), "_aa_bb_cc_" },
+ { strdup("-aa[-bb-]cc-"), "_aa[-bb-]cc_" },
+ { strdup("-aa-[bb]-cc-"), "_aa_[bb]_cc_" },
+ { strdup("-aa-[b-b]-cc-"), "_aa_[b-b]_cc_" },
+ { strdup("-aa-b[-]b-cc"), "_aa_b[-]b_cc" },
+ { }
+ }, *iter;
+
+ for (iter = &teststr[0]; iter->val != NULL; iter++) {
+ _cleanup_free_ char *val = iter->val;
+ underscores(val);
+ assert_return(streq(val, iter->res), EXIT_FAILURE);
+ }
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_underscores,
+ .description = "check implementation of underscores()",
+ .need_spawn = false,
+ );
+
+static int test_path_ends_with_kmod_ext(const struct test *t)
+{
+ struct teststr {
+ const char *val;
+ bool res;
+ } teststr[] = {
+ { "/bla.ko", true },
+#ifdef ENABLE_ZLIB
+ { "/bla.ko.gz", true },
+#endif
+#ifdef ENABLE_XZ
+ { "/bla.ko.xz", true },
+#endif
+#ifdef ENABLE_ZSTD
+ { "/bla.ko.zst", true },
+#endif
+ { "/bla.ko.x", false },
+ { "/bla.ko.", false },
+ { "/bla.koz", false },
+ { "/b", false },
+ { }
+ }, *iter;
+
+ for (iter = &teststr[0]; iter->val != NULL; iter++) {
+ assert_return(path_ends_with_kmod_ext(iter->val,
+ strlen(iter->val)) == iter->res,
+ EXIT_FAILURE);
+ }
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_path_ends_with_kmod_ext,
+ .description = "check implementation of path_ends_with_kmod_ext()",
+ .need_spawn = false,
+ );
+
+#define TEST_WRITE_STR_SAFE_FILE "/write-str-safe"
+#define TEST_WRITE_STR_SAFE_PATH TESTSUITE_ROOTFS "test-util2/" TEST_WRITE_STR_SAFE_FILE
+static int test_write_str_safe(const struct test *t)
+{
+ const char *s = "test";
+ int fd;
+
+ fd = open(TEST_WRITE_STR_SAFE_FILE ".txt", O_CREAT|O_TRUNC|O_WRONLY, 0644);
+ assert_return(fd >= 0, EXIT_FAILURE);
+
+ write_str_safe(fd, s, strlen(s));
+ close(fd);
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_write_str_safe,
+ .description = "check implementation of write_str_safe()",
+ .config = {
+ [TC_ROOTFS] = TESTSUITE_ROOTFS "test-util2/",
+ },
+ .need_spawn = true,
+ .output = {
+ .files = (const struct keyval[]) {
+ { TEST_WRITE_STR_SAFE_PATH ".txt",
+ TEST_WRITE_STR_SAFE_PATH "-correct.txt" },
+ { }
+ },
+ });
+
+static int test_addu64_overflow(const struct test *t)
+{
+ uint64_t res;
+ bool overflow;
+
+ overflow = addu64_overflow(UINT64_MAX - 1, 1, &res);
+ assert_return(!overflow, EXIT_FAILURE);
+ assert_return(res == UINT64_MAX, EXIT_FAILURE);
+
+ overflow = addu64_overflow(UINT64_MAX, 1, &res);
+ assert_return(overflow, EXIT_FAILURE);
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_addu64_overflow,
+ .description = "check implementation of addu4_overflow()",
+ .need_spawn = false,
+ );
+
+
+static int test_backoff_time(const struct test *t)
+{
+ unsigned long long delta = 0;
+
+ /* Check exponential increments */
+ get_backoff_delta_msec(now_msec(), now_msec() + 10, &delta);
+ assert_return(delta == 1, EXIT_FAILURE);
+ get_backoff_delta_msec(now_msec(), now_msec() + 10, &delta);
+ assert_return(delta == 2, EXIT_FAILURE);
+ get_backoff_delta_msec(now_msec(), now_msec() + 10, &delta);
+ assert_return(delta == 4, EXIT_FAILURE);
+ get_backoff_delta_msec(now_msec(), now_msec() + 10, &delta);
+ assert_return(delta == 8, EXIT_FAILURE);
+
+ {
+ unsigned long long t0, tend;
+
+ /* Check tail */
+ delta = 4;
+ tend = now_msec() + 3;
+ t0 = tend - 10;
+ get_backoff_delta_msec(t0, tend, &delta);
+ assert_return(delta == 2, EXIT_FAILURE);
+ tend = now_msec() + 1;
+ t0 = tend - 9;
+ get_backoff_delta_msec(t0, tend, &delta);
+ assert_return(delta == 1, EXIT_FAILURE);
+ tend = now_msec();
+ t0 = tend - 10;
+ get_backoff_delta_msec(t0, tend, &delta);
+ assert_return(delta == 0, EXIT_FAILURE);
+ }
+
+ return EXIT_SUCCESS;
+}
+DEFINE_TEST(test_backoff_time,
+ .description = "check implementation of get_backoff_delta_msec()",
+ .need_spawn = false,
+ );
+
+
+TESTSUITE_MAIN();
diff --git a/testsuite/testsuite.c b/testsuite/testsuite.c
new file mode 100644
index 0000000..318343a
--- /dev/null
+++ b/testsuite/testsuite.c
@@ -0,0 +1,1119 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <shared/util.h>
+
+#include "testsuite.h"
+
+static const char *ANSI_HIGHLIGHT_GREEN_ON = "\x1B[1;32m";
+static const char *ANSI_HIGHLIGHT_YELLOW_ON = "\x1B[1;33m";
+static const char *ANSI_HIGHLIGHT_RED_ON = "\x1B[1;31m";
+static const char *ANSI_HIGHLIGHT_OFF = "\x1B[0m";
+
+static const char *progname;
+static int oneshot = 0;
+static const char options_short[] = "lhn";
+static const struct option options[] = {
+ { "list", no_argument, 0, 'l' },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0 }
+};
+
+#define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/"
+#define TEST_TIMEOUT_USEC 2 * USEC_PER_SEC
+
+static const struct {
+ const char *key;
+ const char *ldpreload;
+} env_config[_TC_LAST] = {
+ [TC_UNAME_R] = { S_TC_UNAME_R, OVERRIDE_LIBDIR "uname.so" },
+ [TC_ROOTFS] = { S_TC_ROOTFS, OVERRIDE_LIBDIR "path.so" },
+ [TC_INIT_MODULE_RETCODES] = { S_TC_INIT_MODULE_RETCODES, OVERRIDE_LIBDIR "init_module.so" },
+ [TC_DELETE_MODULE_RETCODES] = { S_TC_DELETE_MODULE_RETCODES, OVERRIDE_LIBDIR "delete_module.so" },
+};
+
+static void help(void)
+{
+ const struct option *itr;
+ const char *itr_short;
+
+ printf("Usage:\n"
+ "\t%s [options] <test>\n"
+ "Options:\n", basename(progname));
+
+ for (itr = options, itr_short = options_short;
+ itr->name != NULL; itr++, itr_short++)
+ printf("\t-%c, --%s\n", *itr_short, itr->name);
+}
+
+static void test_list(const struct test *start, const struct test *stop)
+{
+ const struct test *t;
+
+ printf("Available tests:\n");
+ for (t = start; t < stop; t++)
+ printf("\t%s, %s\n", t->name, t->description);
+}
+
+int test_init(const struct test *start, const struct test *stop,
+ int argc, char *const argv[])
+{
+ progname = argv[0];
+
+ for (;;) {
+ int c, idx = 0;
+ c = getopt_long(argc, argv, options_short, options, &idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'l':
+ test_list(start, stop);
+ return 0;
+ case 'h':
+ help();
+ return 0;
+ case 'n':
+ oneshot = 1;
+ break;
+ case '?':
+ return -1;
+ default:
+ ERR("unexpected getopt_long() value %c\n", c);
+ return -1;
+ }
+ }
+
+ if (isatty(STDOUT_FILENO) == 0) {
+ ANSI_HIGHLIGHT_OFF = "";
+ ANSI_HIGHLIGHT_RED_ON = "";
+ ANSI_HIGHLIGHT_GREEN_ON = "";
+ }
+
+ return optind;
+}
+
+const struct test *test_find(const struct test *start,
+ const struct test *stop, const char *name)
+{
+ const struct test *t;
+
+ for (t = start; t < stop; t++) {
+ if (streq(t->name, name))
+ return t;
+ }
+
+ return NULL;
+}
+
+static int test_spawn_test(const struct test *t)
+{
+ const char *const args[] = { progname, "-n", t->name, NULL };
+
+ execv(progname, (char *const *) args);
+
+ ERR("failed to spawn %s for %s: %m\n", progname, t->name);
+ return EXIT_FAILURE;
+}
+
+static int test_run_spawned(const struct test *t)
+{
+ int err = t->func(t);
+ exit(err);
+
+ return EXIT_FAILURE;
+}
+
+int test_spawn_prog(const char *prog, const char *const args[])
+{
+ execv(prog, (char *const *) args);
+
+ ERR("failed to spawn %s\n", prog);
+ ERR("did you forget to build tools?\n");
+ return EXIT_FAILURE;
+}
+
+static void test_export_environ(const struct test *t)
+{
+ char *preload = NULL;
+ size_t preloadlen = 0;
+ size_t i;
+ const struct keyval *env;
+
+ unsetenv("LD_PRELOAD");
+
+ for (i = 0; i < _TC_LAST; i++) {
+ const char *ldpreload;
+ size_t ldpreloadlen;
+ char *tmp;
+
+ if (t->config[i] == NULL)
+ continue;
+
+ setenv(env_config[i].key, t->config[i], 1);
+
+ ldpreload = env_config[i].ldpreload;
+ ldpreloadlen = strlen(ldpreload);
+ tmp = realloc(preload, preloadlen + 2 + ldpreloadlen);
+ if (tmp == NULL) {
+ ERR("oom: test_export_environ()\n");
+ return;
+ }
+ preload = tmp;
+
+ if (preloadlen > 0)
+ preload[preloadlen++] = ' ';
+ memcpy(preload + preloadlen, ldpreload, ldpreloadlen);
+ preloadlen += ldpreloadlen;
+ preload[preloadlen] = '\0';
+ }
+
+ if (preload != NULL)
+ setenv("LD_PRELOAD", preload, 1);
+
+ free(preload);
+
+ for (env = t->env_vars; env && env->key; env++)
+ setenv(env->key, env->val, 1);
+}
+
+static inline int test_run_child(const struct test *t, int fdout[2],
+ int fderr[2], int fdmonitor[2])
+{
+ /* kill child if parent dies */
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+
+ test_export_environ(t);
+
+ /* Close read-fds and redirect std{out,err} to the write-fds */
+ if (t->output.out != NULL) {
+ close(fdout[0]);
+ if (dup2(fdout[1], STDOUT_FILENO) < 0) {
+ ERR("could not redirect stdout to pipe: %m\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (t->output.err != NULL) {
+ close(fderr[0]);
+ if (dup2(fderr[1], STDERR_FILENO) < 0) {
+ ERR("could not redirect stderr to pipe: %m\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ close(fdmonitor[0]);
+
+ if (t->config[TC_ROOTFS] != NULL) {
+ const char *stamp = TESTSUITE_ROOTFS "../stamp-rootfs";
+ const char *rootfs = t->config[TC_ROOTFS];
+ struct stat rootfsst, stampst;
+
+ if (stat(stamp, &stampst) != 0) {
+ ERR("could not stat %s\n - %m", stamp);
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat(rootfs, &rootfsst) != 0) {
+ ERR("could not stat %s\n - %m", rootfs);
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat_mstamp(&rootfsst) > stat_mstamp(&stampst)) {
+ ERR("rootfs %s is dirty, please run 'make rootfs' before runnning this test\n",
+ rootfs);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (t->need_spawn)
+ return test_spawn_test(t);
+ else
+ return test_run_spawned(t);
+}
+
+#define BUFSZ 4096
+
+enum fd_cmp_type {
+ FD_CMP_MONITOR,
+ FD_CMP_OUT,
+ FD_CMP_ERR,
+ FD_CMP_MAX = FD_CMP_ERR,
+};
+
+struct fd_cmp {
+ enum fd_cmp_type type;
+ int fd;
+ int fd_match;
+ bool activity;
+ const char *path;
+ const char *name;
+ char buf[BUFSZ];
+ char buf_match[BUFSZ];
+ unsigned int head;
+ unsigned int head_match;
+};
+
+static int fd_cmp_check_activity(struct fd_cmp *fd_cmp)
+{
+ struct stat st;
+
+ /* not monitoring or monitoring and it has activity */
+ if (fd_cmp == NULL || fd_cmp->fd < 0 || fd_cmp->activity)
+ return 0;
+
+ /* monitoring, there was no activity and size matches */
+ if (stat(fd_cmp->path, &st) == 0 && st.st_size == 0)
+ return 0;
+
+ ERR("Expecting output on %s, but test didn't produce any\n",
+ fd_cmp->name);
+
+ return -1;
+}
+
+static bool fd_cmp_is_active(struct fd_cmp *fd_cmp)
+{
+ return fd_cmp->fd != -1;
+}
+
+static int fd_cmp_open_monitor(struct fd_cmp *fd_cmp, int fd, int fd_ep)
+{
+ struct epoll_event ep = {};
+
+ ep.events = EPOLLHUP;
+ ep.data.ptr = fd_cmp;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd, &ep) < 0) {
+ ERR("could not add monitor fd to epoll: %m\n");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int fd_cmp_open_std(struct fd_cmp *fd_cmp,
+ const char *fn, int fd, int fd_ep)
+{
+ struct epoll_event ep = {};
+ int fd_match;
+
+ fd_match = open(fn, O_RDONLY);
+ if (fd_match < 0) {
+ ERR("could not open %s for read: %m\n", fn);
+ return -errno;
+ }
+ ep.events = EPOLLIN;
+ ep.data.ptr = fd_cmp;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd, &ep) < 0) {
+ ERR("could not add fd to epoll: %m\n");
+ close(fd_match);
+ return -errno;
+ }
+
+ return fd_match;
+}
+
+/* opens output file AND adds descriptor to epoll */
+static int fd_cmp_open(struct fd_cmp **fd_cmp_out,
+ enum fd_cmp_type type, const char *fn, int fd,
+ int fd_ep)
+{
+ int err = 0;
+ struct fd_cmp *fd_cmp;
+
+ fd_cmp = calloc(1, sizeof(*fd_cmp));
+ if (fd_cmp == NULL) {
+ ERR("could not allocate fd_cmp\n");
+ return -ENOMEM;
+ }
+
+ switch (type) {
+ case FD_CMP_MONITOR:
+ err = fd_cmp_open_monitor(fd_cmp, fd, fd_ep);
+ break;
+ case FD_CMP_OUT:
+ fd_cmp->name = "STDOUT";
+ err = fd_cmp_open_std(fd_cmp, fn, fd, fd_ep);
+ break;
+ case FD_CMP_ERR:
+ fd_cmp->name = "STDERR";
+ err = fd_cmp_open_std(fd_cmp, fn, fd, fd_ep);
+ break;
+ default:
+ ERR("unknown fd type %d\n", type);
+ err = -1;
+ }
+
+ if (err < 0) {
+ free(fd_cmp);
+ return err;
+ }
+
+ fd_cmp->fd_match = err;
+ fd_cmp->fd = fd;
+ fd_cmp->type = type;
+ fd_cmp->path = fn;
+
+ *fd_cmp_out = fd_cmp;
+ return 0;
+}
+
+static int fd_cmp_check_ev_in(struct fd_cmp *fd_cmp)
+{
+ if (fd_cmp->type == FD_CMP_MONITOR) {
+ ERR("Unexpected activity on monitor pipe\n");
+ return -EINVAL;
+ }
+ fd_cmp->activity = true;
+
+ return 0;
+}
+
+static void fd_cmp_delete_ep(struct fd_cmp *fd_cmp, int fd_ep)
+{
+ if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_cmp->fd, NULL) < 0) {
+ ERR("could not remove fd %d from epoll: %m\n", fd_cmp->fd);
+ }
+ fd_cmp->fd = -1;
+}
+
+static void fd_cmp_close(struct fd_cmp *fd_cmp)
+{
+ if (fd_cmp == NULL)
+ return;
+
+ if (fd_cmp->fd >= 0)
+ close(fd_cmp->fd);
+ free(fd_cmp);
+}
+
+static bool fd_cmp_regex_one(const char *pattern, const char *s)
+{
+ _cleanup_(regfree) regex_t re = { };
+
+ return !regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) &&
+ !regexec(&re, s, 0, NULL, 0);
+}
+
+/*
+ * read fd and fd_match, checking the first matches the regex of the second,
+ * line by line
+ */
+static bool fd_cmp_regex(struct fd_cmp *fd_cmp, const struct test *t)
+{
+ char *p, *p_match;
+ int done = 0, done_match = 0, r;
+
+ if (fd_cmp->head >= sizeof(fd_cmp->buf)) {
+ ERR("Read %zu bytes without a newline\n", sizeof(fd_cmp->buf));
+ ERR("output: %.*s", (int)sizeof(fd_cmp->buf), fd_cmp->buf);
+ return false;
+ }
+
+ r = read(fd_cmp->fd, fd_cmp->buf + fd_cmp->head,
+ sizeof(fd_cmp->buf) - fd_cmp->head);
+ if (r <= 0)
+ return true;
+
+ fd_cmp->head += r;
+
+ /*
+ * Process as many lines as read from fd and that fits in the buffer -
+ * it's assumed that if we get N lines from fd, we should be able to
+ * get the same amount from fd_match
+ */
+ for (;;) {
+ p = memchr(fd_cmp->buf + done, '\n', fd_cmp->head - done);
+ if (!p)
+ break;
+ *p = '\0';
+
+ p_match = memchr(fd_cmp->buf_match + done_match, '\n',
+ fd_cmp->head_match - done_match);
+ if (!p_match) {
+ if (fd_cmp->head_match >= sizeof(fd_cmp->buf_match)) {
+ ERR("Read %zu bytes without a match\n", sizeof(fd_cmp->buf_match));
+ ERR("output: %.*s", (int)sizeof(fd_cmp->buf_match), fd_cmp->buf_match);
+ return false;
+ }
+
+ /* pump more data from file */
+ r = read(fd_cmp->fd_match, fd_cmp->buf_match + fd_cmp->head_match,
+ sizeof(fd_cmp->buf_match) - fd_cmp->head_match);
+ if (r <= 0) {
+ ERR("could not read match fd %d\n", fd_cmp->fd_match);
+ return false;
+ }
+ fd_cmp->head_match += r;
+ p_match = memchr(fd_cmp->buf_match + done_match, '\n',
+ fd_cmp->head_match - done_match);
+ if (!p_match) {
+ ERR("could not find match line from fd %d\n", fd_cmp->fd_match);
+ return false;
+ }
+ }
+ *p_match = '\0';
+
+ if (!fd_cmp_regex_one(fd_cmp->buf_match + done_match, fd_cmp->buf + done)) {
+ ERR("Output does not match pattern on %s:\n", fd_cmp->name);
+ ERR("pattern: %s\n", fd_cmp->buf_match + done_match);
+ ERR("output : %s\n", fd_cmp->buf + done);
+ return false;
+ }
+
+ done = p - fd_cmp->buf + 1;
+ done_match = p_match - fd_cmp->buf_match + 1;
+ }
+
+ /*
+ * Prepare for the next call: anything we processed we remove from the
+ * buffer by memmoving the remaining bytes up to the beginning
+ */
+ if (done) {
+ if (fd_cmp->head - done)
+ memmove(fd_cmp->buf, fd_cmp->buf + done, fd_cmp->head - done);
+ fd_cmp->head -= done;
+ }
+
+ if (done_match) {
+ if (fd_cmp->head_match - done_match)
+ memmove(fd_cmp->buf_match, fd_cmp->buf_match + done_match,
+ fd_cmp->head_match - done_match);
+ fd_cmp->head_match -= done_match;
+ }
+
+ return true;
+}
+
+/* read fd and fd_match, checking they match exactly */
+static bool fd_cmp_exact(struct fd_cmp *fd_cmp, const struct test *t)
+{
+ int r, rmatch, done = 0;
+
+ r = read(fd_cmp->fd, fd_cmp->buf, sizeof(fd_cmp->buf) - 1);
+ if (r <= 0)
+ /* try again later */
+ return true;
+
+ /* read as much data from fd_match as we read from fd */
+ for (;;) {
+ rmatch = read(fd_cmp->fd_match, fd_cmp->buf_match + done, r - done);
+ if (rmatch == 0)
+ break;
+
+ if (rmatch < 0) {
+ if (errno == EINTR)
+ continue;
+ ERR("could not read match fd %d\n", fd_cmp->fd_match);
+ return false;
+ }
+
+ done += rmatch;
+ }
+
+ fd_cmp->buf[r] = '\0';
+ fd_cmp->buf_match[r] = '\0';
+
+ if (t->print_outputs)
+ printf("%s: %s\n", fd_cmp->name, fd_cmp->buf);
+
+ if (!streq(fd_cmp->buf, fd_cmp->buf_match)) {
+ ERR("Outputs do not match on %s:\n", fd_cmp->name);
+ ERR("correct:\n%s\n", fd_cmp->buf_match);
+ ERR("wrong:\n%s\n", fd_cmp->buf);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_run_parent_check_outputs(const struct test *t,
+ int fdout, int fderr, int fdmonitor,
+ pid_t child)
+{
+ int err, fd_ep;
+ unsigned long long end_usec, start_usec;
+ struct fd_cmp *fd_cmp_out = NULL;
+ struct fd_cmp *fd_cmp_err = NULL;
+ struct fd_cmp *fd_cmp_monitor = NULL;
+ int n_fd = 0;
+
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ ERR("could not create epoll fd: %m\n");
+ return false;
+ }
+
+ if (t->output.out != NULL) {
+ err = fd_cmp_open(&fd_cmp_out,
+ FD_CMP_OUT, t->output.out, fdout, fd_ep);
+ if (err < 0)
+ goto out;
+ n_fd++;
+ }
+
+ if (t->output.err != NULL) {
+ err = fd_cmp_open(&fd_cmp_err,
+ FD_CMP_ERR, t->output.err, fderr, fd_ep);
+ if (err < 0)
+ goto out;
+ n_fd++;
+ }
+
+ err = fd_cmp_open(&fd_cmp_monitor, FD_CMP_MONITOR, NULL, fdmonitor, fd_ep);
+ if (err < 0)
+ goto out;
+ n_fd++;
+
+ start_usec = now_usec();
+ end_usec = start_usec + TEST_TIMEOUT_USEC;
+
+ for (err = 0; n_fd > 0;) {
+ int fdcount, i, timeout;
+ struct epoll_event ev[4];
+ unsigned long long curr_usec = now_usec();
+
+ if (curr_usec > end_usec)
+ break;
+
+ timeout = (end_usec - curr_usec) / USEC_PER_MSEC;
+ fdcount = epoll_wait(fd_ep, ev, 4, timeout);
+ if (fdcount < 0) {
+ if (errno == EINTR)
+ continue;
+ err = -errno;
+ ERR("could not poll: %m\n");
+ goto out;
+ }
+
+ for (i = 0; i < fdcount; i++) {
+ struct fd_cmp *fd_cmp = ev[i].data.ptr;
+ bool ret;
+
+ if (ev[i].events & EPOLLIN) {
+ err = fd_cmp_check_ev_in(fd_cmp);
+ if (err < 0)
+ goto out;
+
+ if (t->output.regex)
+ ret = fd_cmp_regex(fd_cmp, t);
+ else
+ ret = fd_cmp_exact(fd_cmp, t);
+
+ if (!ret) {
+ err = -1;
+ goto out;
+ }
+ } else if (ev[i].events & EPOLLHUP) {
+ fd_cmp_delete_ep(fd_cmp, fd_ep);
+ n_fd--;
+ }
+ }
+ }
+
+ err = fd_cmp_check_activity(fd_cmp_out);
+ err |= fd_cmp_check_activity(fd_cmp_err);
+
+ if (err == 0 && fd_cmp_is_active(fd_cmp_monitor)) {
+ err = -EINVAL;
+ ERR("Test '%s' timed out, killing %d\n", t->name, child);
+ kill(child, SIGKILL);
+ }
+
+out:
+ fd_cmp_close(fd_cmp_out);
+ fd_cmp_close(fd_cmp_err);
+ fd_cmp_close(fd_cmp_monitor);
+ close(fd_ep);
+
+ return err == 0;
+}
+
+static inline int safe_read(int fd, void *buf, size_t count)
+{
+ int r;
+
+ while (1) {
+ r = read(fd, buf, count);
+ if (r == -1 && errno == EINTR)
+ continue;
+ break;
+ }
+
+ return r;
+}
+
+static bool check_generated_files(const struct test *t)
+{
+ const struct keyval *k;
+
+ /* This is not meant to be a diff replacement, just stupidly check if
+ * the files match. Bear in mind they can be binary files */
+ for (k = t->output.files; k && k->key; k++) {
+ struct stat sta, stb;
+ int fda = -1, fdb = -1;
+ char bufa[4096];
+ char bufb[4096];
+
+ fda = open(k->key, O_RDONLY);
+ if (fda < 0) {
+ ERR("could not open %s\n - %m\n", k->key);
+ goto fail;
+ }
+
+ fdb = open(k->val, O_RDONLY);
+ if (fdb < 0) {
+ ERR("could not open %s\n - %m\n", k->val);
+ goto fail;
+ }
+
+ if (fstat(fda, &sta) != 0) {
+ ERR("could not fstat %d %s\n - %m\n", fda, k->key);
+ goto fail;
+ }
+
+ if (fstat(fdb, &stb) != 0) {
+ ERR("could not fstat %d %s\n - %m\n", fdb, k->key);
+ goto fail;
+ }
+
+ if (sta.st_size != stb.st_size) {
+ ERR("sizes do not match %s %s\n", k->key, k->val);
+ goto fail;
+ }
+
+ for (;;) {
+ int r, done;
+
+ r = safe_read(fda, bufa, sizeof(bufa));
+ if (r < 0)
+ goto fail;
+
+ if (r == 0)
+ /* size is already checked, go to next file */
+ goto next;
+
+ for (done = 0; done < r;) {
+ int r2 = safe_read(fdb, bufb + done, r - done);
+
+ if (r2 <= 0)
+ goto fail;
+
+ done += r2;
+ }
+
+ if (memcmp(bufa, bufb, r) != 0)
+ goto fail;
+ }
+
+next:
+ close(fda);
+ close(fdb);
+ continue;
+
+fail:
+ if (fda >= 0)
+ close(fda);
+ if (fdb >= 0)
+ close(fdb);
+
+ return false;
+ }
+
+ return true;
+}
+
+static int cmp_modnames(const void *m1, const void *m2)
+{
+ const char *s1 = *(char *const *)m1;
+ const char *s2 = *(char *const *)m2;
+ int i;
+
+ for (i = 0; s1[i] || s2[i]; i++) {
+ char c1 = s1[i], c2 = s2[i];
+ if (c1 == '-')
+ c1 = '_';
+ if (c2 == '-')
+ c2 = '_';
+ if (c1 != c2)
+ return c1 - c2;
+ }
+ return 0;
+}
+
+/*
+ * Store the expected module names in buf and return a list of pointers to
+ * them.
+ */
+static const char **read_expected_modules(const struct test *t,
+ char **buf, int *count)
+{
+ const char **res;
+ int len;
+ int i;
+ char *p;
+
+ if (t->modules_loaded[0] == '\0') {
+ *count = 0;
+ *buf = NULL;
+ return NULL;
+ }
+ *buf = strdup(t->modules_loaded);
+ if (!*buf) {
+ *count = -1;
+ return NULL;
+ }
+ len = 1;
+ for (p = *buf; *p; p++)
+ if (*p == ',')
+ len++;
+ res = malloc(sizeof(char *) * len);
+ if (!res) {
+ perror("malloc");
+ *count = -1;
+ free(*buf);
+ *buf = NULL;
+ return NULL;
+ }
+ i = 0;
+ res[i++] = *buf;
+ for (p = *buf; i < len; p++)
+ if (*p == ',') {
+ *p = '\0';
+ res[i++] = p + 1;
+ }
+ *count = len;
+ return res;
+}
+
+static char **read_loaded_modules(const struct test *t, char **buf, int *count)
+{
+ char dirname[PATH_MAX];
+ DIR *dir;
+ struct dirent *dirent;
+ int i;
+ int len = 0, bufsz;
+ char **res = NULL;
+ char *p;
+ const char *rootfs = t->config[TC_ROOTFS] ? t->config[TC_ROOTFS] : "";
+
+ /* Store the entries in /sys/module to res */
+ if (snprintf(dirname, sizeof(dirname), "%s/sys/module", rootfs)
+ >= (int)sizeof(dirname)) {
+ ERR("rootfs path too long: %s\n", rootfs);
+ *buf = NULL;
+ len = -1;
+ goto out;
+ }
+ dir = opendir(dirname);
+ /* not an error, simply return empty list */
+ if (!dir) {
+ *buf = NULL;
+ goto out;
+ }
+ bufsz = 0;
+ while ((dirent = readdir(dir))) {
+ if (dirent->d_name[0] == '.')
+ continue;
+ len++;
+ bufsz += strlen(dirent->d_name) + 1;
+ }
+ res = malloc(sizeof(char *) * len);
+ if (!res) {
+ perror("malloc");
+ len = -1;
+ goto out_dir;
+ }
+ *buf = malloc(bufsz);
+ if (!*buf) {
+ perror("malloc");
+ free(res);
+ res = NULL;
+ len = -1;
+ goto out_dir;
+ }
+ rewinddir(dir);
+ i = 0;
+ p = *buf;
+ while ((dirent = readdir(dir))) {
+ int size;
+
+ if (dirent->d_name[0] == '.')
+ continue;
+ size = strlen(dirent->d_name) + 1;
+ memcpy(p, dirent->d_name, size);
+ res[i++] = p;
+ p += size;
+ }
+out_dir:
+ closedir(dir);
+out:
+ *count = len;
+ return res;
+}
+
+static int check_loaded_modules(const struct test *t)
+{
+ int l1, l2, i1, i2;
+ const char **a1;
+ char **a2;
+ char *buf1, *buf2;
+ int err = false;
+
+ a1 = read_expected_modules(t, &buf1, &l1);
+ if (l1 < 0)
+ return err;
+ a2 = read_loaded_modules(t, &buf2, &l2);
+ if (l2 < 0)
+ goto out_a1;
+ qsort(a1, l1, sizeof(char *), cmp_modnames);
+ qsort(a2, l2, sizeof(char *), cmp_modnames);
+ i1 = i2 = 0;
+ err = true;
+ while (i1 < l1 || i2 < l2) {
+ int cmp;
+
+ if (i1 >= l1)
+ cmp = 1;
+ else if (i2 >= l2)
+ cmp = -1;
+ else
+ cmp = cmp_modnames(&a1[i1], &a2[i2]);
+ if (cmp == 0) {
+ i1++;
+ i2++;
+ } else if (cmp < 0) {
+ err = false;
+ ERR("module %s not loaded\n", a1[i1]);
+ i1++;
+ } else {
+ err = false;
+ ERR("module %s is loaded but should not be \n", a2[i2]);
+ i2++;
+ }
+ }
+ free(a2);
+ free(buf2);
+out_a1:
+ free(a1);
+ free(buf1);
+ return err;
+}
+
+static inline int test_run_parent(const struct test *t, int fdout[2],
+ int fderr[2], int fdmonitor[2], pid_t child)
+{
+ pid_t pid;
+ int err;
+ bool matchout, match_modules;
+
+ if (t->skip) {
+ LOG("%sSKIPPED%s: %s\n",
+ ANSI_HIGHLIGHT_YELLOW_ON, ANSI_HIGHLIGHT_OFF,
+ t->name);
+ err = EXIT_SUCCESS;
+ goto exit;
+ }
+
+ /* Close write-fds */
+ if (t->output.out != NULL)
+ close(fdout[1]);
+ if (t->output.err != NULL)
+ close(fderr[1]);
+ close(fdmonitor[1]);
+
+ matchout = test_run_parent_check_outputs(t, fdout[0], fderr[0],
+ fdmonitor[0], child);
+
+ /*
+ * break pipe on the other end: either child already closed or we want
+ * to stop it
+ */
+ if (t->output.out != NULL)
+ close(fdout[0]);
+ if (t->output.err != NULL)
+ close(fderr[0]);
+ close(fdmonitor[0]);
+
+ do {
+ pid = wait(&err);
+ if (pid == -1) {
+ ERR("error waitpid(): %m\n");
+ err = EXIT_FAILURE;
+ goto exit;
+ }
+ } while (!WIFEXITED(err) && !WIFSIGNALED(err));
+
+ if (WIFEXITED(err)) {
+ if (WEXITSTATUS(err) != 0)
+ ERR("'%s' [%u] exited with return code %d\n",
+ t->name, pid, WEXITSTATUS(err));
+ else
+ LOG("'%s' [%u] exited with return code %d\n",
+ t->name, pid, WEXITSTATUS(err));
+ } else if (WIFSIGNALED(err)) {
+ ERR("'%s' [%u] terminated by signal %d (%s)\n", t->name, pid,
+ WTERMSIG(err), strsignal(WTERMSIG(err)));
+ err = t->expected_fail ? EXIT_SUCCESS : EXIT_FAILURE;
+ goto exit;
+ }
+
+ if (matchout)
+ matchout = check_generated_files(t);
+ if (t->modules_loaded)
+ match_modules = check_loaded_modules(t);
+ else
+ match_modules = true;
+
+ if (t->expected_fail == false) {
+ if (err == 0) {
+ if (matchout && match_modules)
+ LOG("%sPASSED%s: %s\n",
+ ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
+ t->name);
+ else {
+ ERR("%sFAILED%s: exit ok but %s do not match: %s\n",
+ ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
+ matchout ? "loaded modules" : "outputs",
+ t->name);
+ err = EXIT_FAILURE;
+ }
+ } else {
+ ERR("%sFAILED%s: %s\n",
+ ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
+ t->name);
+ }
+ } else {
+ if (err == 0) {
+ if (matchout) {
+ ERR("%sUNEXPECTED PASS%s: exit with 0: %s\n",
+ ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
+ t->name);
+ err = EXIT_FAILURE;
+ } else {
+ ERR("%sUNEXPECTED PASS%s: exit with 0 and outputs do not match: %s\n",
+ ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
+ t->name);
+ err = EXIT_FAILURE;
+ }
+ } else {
+ if (matchout) {
+ LOG("%sEXPECTED FAIL%s: %s\n",
+ ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
+ t->name);
+ err = EXIT_SUCCESS;
+ } else {
+ LOG("%sEXPECTED FAIL%s: exit with %d but outputs do not match: %s\n",
+ ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
+ WEXITSTATUS(err), t->name);
+ err = EXIT_FAILURE;
+ }
+ }
+ }
+
+exit:
+ LOG("------\n");
+ return err;
+}
+
+static int prepend_path(const char *extra)
+{
+ char *oldpath, *newpath;
+ int r;
+
+ if (extra == NULL)
+ return 0;
+
+ oldpath = getenv("PATH");
+ if (oldpath == NULL)
+ return setenv("PATH", extra, 1);
+
+ if (asprintf(&newpath, "%s:%s", extra, oldpath) < 0) {
+ ERR("failed to allocate memory to new PATH\n");
+ return -1;
+ }
+
+ r = setenv("PATH", newpath, 1);
+ free(newpath);
+
+ return r;
+}
+
+int test_run(const struct test *t)
+{
+ pid_t pid;
+ int fdout[2];
+ int fderr[2];
+ int fdmonitor[2];
+
+ if (t->need_spawn && oneshot)
+ test_run_spawned(t);
+
+ if (t->output.out != NULL) {
+ if (pipe(fdout) != 0) {
+ ERR("could not create out pipe for %s\n", t->name);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (t->output.err != NULL) {
+ if (pipe(fderr) != 0) {
+ ERR("could not create err pipe for %s\n", t->name);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (pipe(fdmonitor) != 0) {
+ ERR("could not create monitor pipe for %s\n", t->name);
+ return EXIT_FAILURE;
+ }
+
+ if (prepend_path(t->path) < 0) {
+ ERR("failed to prepend '%s' to PATH\n", t->path);
+ return EXIT_FAILURE;
+ }
+
+ LOG("running %s, in forked context\n", t->name);
+
+ pid = fork();
+ if (pid < 0) {
+ ERR("could not fork(): %m\n");
+ LOG("FAILED: %s\n", t->name);
+ return EXIT_FAILURE;
+ }
+
+ if (pid > 0)
+ return test_run_parent(t, fdout, fderr, fdmonitor, pid);
+
+ return test_run_child(t, fdout, fderr, fdmonitor);
+}
diff --git a/testsuite/testsuite.h b/testsuite/testsuite.h
new file mode 100644
index 0000000..44d1730
--- /dev/null
+++ b/testsuite/testsuite.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <shared/macro.h>
+
+struct test;
+typedef int (*testfunc)(const struct test *t);
+
+enum test_config {
+ /*
+ * Where's the roots dir for this test. It will LD_PRELOAD path.so in
+ * order to trap calls to functions using paths.
+ */
+ TC_ROOTFS = 0,
+
+ /*
+ * What's the desired string to be returned by `uname -r`. It will
+ * trap calls to uname(3P) by LD_PRELOAD'ing uname.so and then filling
+ * in the information in u.release.
+ */
+ TC_UNAME_R,
+
+ /*
+ * Fake calls to init_module(2), returning return-code and setting
+ * errno to err-code. Set this variable with the following format:
+ *
+ * modname:return-code:err-code
+ *
+ * When this variable is used, all calls to init_module() are trapped
+ * and by default the return code is 0. In other words, they fake
+ * "success" for all modules, except the ones in the list above, for
+ * which the return codes are used.
+ */
+ TC_INIT_MODULE_RETCODES,
+
+ /*
+ * Fake calls to delete_module(2), returning return-code and setting
+ * errno to err-code. Set this variable with the following format:
+ *
+ * modname:return-code:err-code
+ *
+ * When this variable is used, all calls to init_module() are trapped
+ * and by default the return code is 0. In other words, they fake
+ * "success" for all modules, except the ones in the list above, for
+ * which the return codes are used.
+ */
+ TC_DELETE_MODULE_RETCODES,
+
+ _TC_LAST,
+};
+
+#define S_TC_ROOTFS "TESTSUITE_ROOTFS"
+#define S_TC_UNAME_R "TESTSUITE_UNAME_R"
+#define S_TC_INIT_MODULE_RETCODES "TESTSUITE_INIT_MODULE_RETCODES"
+#define S_TC_DELETE_MODULE_RETCODES "TESTSUITE_DELETE_MODULE_RETCODES"
+
+struct keyval {
+ const char *key;
+ const char *val;
+};
+
+struct test {
+ const char *name;
+ const char *description;
+ struct {
+ /* File with correct stdout */
+ const char *out;
+ /* File with correct stderr */
+ const char *err;
+
+ /*
+ * whether to treat the correct files as regex to the real
+ * output
+ */
+ bool regex;
+
+ /*
+ * Vector with pair of files
+ * key = correct file
+ * val = file to check
+ */
+ const struct keyval *files;
+ } output;
+ /* comma-separated list of loaded modules at the end of the test */
+ const char *modules_loaded;
+ testfunc func;
+ const char *config[_TC_LAST];
+ const char *path;
+ const struct keyval *env_vars;
+ bool need_spawn;
+ bool expected_fail;
+ /* allow to skip tests that don't meet compile-time dependencies */
+ bool skip;
+ bool print_outputs;
+} __attribute__((aligned(8)));
+
+
+int test_init(const struct test *start, const struct test *stop,
+ int argc, char *const argv[]);
+const struct test *test_find(const struct test *start, const struct test *stop,
+ const char *name);
+int test_spawn_prog(const char *prog, const char *const args[]);
+int test_run(const struct test *t);
+
+#define TS_EXPORT __attribute__ ((visibility("default")))
+
+#define _LOG(prefix, fmt, ...) printf("TESTSUITE: " prefix fmt, ## __VA_ARGS__)
+#define LOG(fmt, ...) _LOG("", fmt, ## __VA_ARGS__)
+#define WARN(fmt, ...) _LOG("WARN: ", fmt, ## __VA_ARGS__)
+#define ERR(fmt, ...) _LOG("ERR: ", fmt, ## __VA_ARGS__)
+
+#define assert_return(expr, r) \
+ do { \
+ if ((!(expr))) { \
+ ERR("Failed assertion: " #expr " %s:%d %s\n", \
+ __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ return (r); \
+ } \
+ } while (false)
+
+
+/* Test definitions */
+#define DEFINE_TEST_WITH_FUNC(_name, _func, ...) \
+ static const struct test UNIQ(s##_name) \
+ __attribute__((used, section("kmod_tests"), aligned(8))) = { \
+ .name = #_name, \
+ .func = _func, \
+ ## __VA_ARGS__ \
+ };
+
+#define DEFINE_TEST(_name, ...) DEFINE_TEST_WITH_FUNC(_name, _name, __VA_ARGS__)
+
+#define TESTSUITE_MAIN() \
+ extern struct test __start_kmod_tests[] __attribute__((weak, visibility("hidden"))); \
+ extern struct test __stop_kmod_tests[] __attribute__((weak, visibility("hidden"))); \
+ int main(int argc, char *argv[]) \
+ { \
+ const struct test *t; \
+ int arg; \
+ \
+ arg = test_init(__start_kmod_tests, __stop_kmod_tests, argc, argv); \
+ if (arg == 0) \
+ return 0; \
+ if (arg < 0) \
+ return EXIT_FAILURE; \
+ \
+ if (arg < argc) { \
+ t = test_find(__start_kmod_tests, __stop_kmod_tests, argv[arg]); \
+ if (t == NULL) { \
+ fprintf(stderr, "could not find test %s\n", argv[arg]); \
+ exit(EXIT_FAILURE); \
+ } \
+ \
+ return test_run(t); \
+ } \
+ \
+ for (t = __start_kmod_tests; t < __stop_kmod_tests; t++) { \
+ if (test_run(t) != 0) \
+ exit(EXIT_FAILURE); \
+ } \
+ \
+ exit(EXIT_SUCCESS); \
+ } \
+
+#ifdef noreturn
+# define __noreturn noreturn
+#elif __STDC_VERSION__ >= 201112L
+# define __noreturn _Noreturn
+#else
+# define __noreturn __attribute__((noreturn))
+#endif
diff --git a/testsuite/uname.c b/testsuite/uname.c
new file mode 100644
index 0000000..27f92d5
--- /dev/null
+++ b/testsuite/uname.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012-2013 ProFUSION embedded systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "testsuite.h"
+
+TS_EXPORT int uname(struct utsname *u)
+{
+ static void *nextlib = NULL;
+ static int (*nextlib_uname)(struct utsname *u);
+ const char *release;
+ int err;
+ size_t sz;
+
+ if (nextlib == NULL) {
+#ifdef RTLD_NEXT
+ nextlib = RTLD_NEXT;
+#else
+ nextlib = dlopen("libc.so.6", RTLD_LAZY);
+#endif
+ nextlib_uname = dlsym(nextlib, "uname");
+ }
+
+ err = nextlib_uname(u);
+ if (err < 0)
+ return err;
+
+ if (!environ)
+ /*
+ * probably called from within glibc before main(); unsafe
+ * to call getenv()
+ */
+ return 0;
+
+ release = getenv(S_TC_UNAME_R);
+ if (release == NULL) {
+ fprintf(stderr, "TRAP uname(): missing export %s?\n",
+ S_TC_UNAME_R);
+ return 0;
+ }
+
+ sz = strlen(release) + 1;
+ if (sz > sizeof(u->release)) {
+ fprintf(stderr, "uname(): sizeof release (%s) "
+ "is greater than available space: %zu",
+ release, sizeof(u->release));
+ errno = -EFAULT;
+ return -1;
+ }
+
+ memcpy(u->release, release, sz);
+ return 0;
+}
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000..3e34160
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1,15 @@
+/.dirstamp
+/depmod
+/insmod
+/kmod
+/kmod-depmod
+/kmod-insmod
+/kmod-lsmod
+/kmod-modinfo
+/kmod-modprobe
+/kmod-rmmod
+/lsmod
+/modinfo
+/modprobe
+/rmmod
+/test
diff --git a/tools/COPYING b/tools/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/tools/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 0000000..223bec2
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,13 @@
+# Copyright 2010 Lennart Poettering
+#
+# This file has been copied from systemd. It is a dirty trick to simplify
+# compilation when CWD is not the root of the source tree. This file is not
+# intended to be distributed. So, don't touch it, even better ignore it!
+
+all:
+ $(MAKE) -C ..
+
+clean:
+ $(MAKE) -C .. clean
+
+.PHONY: all clean
diff --git a/tools/depmod.c b/tools/depmod.c
new file mode 100644
index 0000000..43fc354
--- /dev/null
+++ b/tools/depmod.c
@@ -0,0 +1,3153 @@
+/*
+ * kmod-depmod - calculate modules.dep using libkmod.
+ *
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+
+#include <shared/array.h>
+#include <shared/hash.h>
+#include <shared/macro.h>
+#include <shared/util.h>
+#include <shared/scratchbuf.h>
+
+#include <libkmod/libkmod-internal.h>
+
+#undef ERR
+#undef DBG
+
+#include "kmod.h"
+
+#define DEFAULT_VERBOSE LOG_WARNING
+static int verbose = DEFAULT_VERBOSE;
+
+static const char CFG_BUILTIN_KEY[] = "built-in";
+static const char CFG_EXTERNAL_KEY[] = "external";
+static const char *const default_cfg_paths[] = {
+ SYSCONFDIR "/depmod.d",
+ "/run/depmod.d",
+ "/usr/local/lib/depmod.d",
+ DISTCONFDIR "/depmod.d",
+ "/lib/depmod.d",
+ NULL
+};
+
+static const char cmdopts_s[] = "aAb:o:C:E:F:euqrvnP:wmVh";
+static const struct option cmdopts[] = {
+ { "all", no_argument, 0, 'a' },
+ { "quick", no_argument, 0, 'A' },
+ { "basedir", required_argument, 0, 'b' },
+ { "outdir", required_argument, 0, 'o' },
+ { "config", required_argument, 0, 'C' },
+ { "symvers", required_argument, 0, 'E' },
+ { "filesyms", required_argument, 0, 'F' },
+ { "errsyms", no_argument, 0, 'e' },
+ { "unresolved-error", no_argument, 0, 'u' }, /* deprecated */
+ { "quiet", no_argument, 0, 'q' }, /* deprecated */
+ { "root", no_argument, 0, 'r' }, /* deprecated */
+ { "verbose", no_argument, 0, 'v' },
+ { "show", no_argument, 0, 'n' },
+ { "dry-run", no_argument, 0, 'n' },
+ { "symbol-prefix", required_argument, 0, 'P' },
+ { "warn", no_argument, 0, 'w' },
+ { "map", no_argument, 0, 'm' }, /* deprecated */
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'h' },
+ { }
+};
+
+static void help(void)
+{
+ printf("Usage:\n"
+ "\t%s -[aA] [options] [forced_version]\n"
+ "\n"
+ "If no arguments (except options) are given, \"depmod -a\" is assumed\n"
+ "\n"
+ "depmod will output a dependency list suitable for the modprobe utility.\n"
+ "\n"
+ "Options:\n"
+ "\t-a, --all Probe all modules\n"
+ "\t-A, --quick Only does the work if there's a new module\n"
+ "\t-e, --errsyms Report not supplied symbols\n"
+ "\t-n, --show Write the dependency file on stdout only\n"
+ "\t-P, --symbol-prefix Architecture symbol prefix\n"
+ "\t-C, --config=PATH Read configuration from PATH\n"
+ "\t-v, --verbose Enable verbose mode\n"
+ "\t-w, --warn Warn on duplicates\n"
+ "\t-V, --version show version\n"
+ "\t-h, --help show this help\n"
+ "\n"
+ "The following options are useful for people managing distributions:\n"
+ "\t-b, --basedir=DIR Use an image of a module tree.\n"
+ "\t-o, --outdir=DIR Output directory for generated files.\n"
+ "\t-F, --filesyms=FILE Use the file instead of the\n"
+ "\t current kernel symbols.\n"
+ "\t-E, --symvers=FILE Use Module.symvers file to check\n"
+ "\t symbol versions.\n",
+ program_invocation_short_name);
+}
+
+_printf_format_(1, 2)
+static inline void _show(const char *fmt, ...)
+{
+ va_list args;
+
+ if (verbose <= DEFAULT_VERBOSE)
+ return;
+
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ fflush(stdout);
+ va_end(args);
+}
+#define SHOW(...) _show(__VA_ARGS__)
+
+
+/* binary index write *************************************************/
+#include <arpa/inet.h>
+/* BEGIN: code from module-init-tools/index.c just modified to compile here.
+ *
+ * Original copyright:
+ * index.c: module index file shared functions for modprobe and depmod
+ * Copyright (C) 2008 Alan Jenkins <alan-jenkins@tuffmail.co.uk>.
+ *
+ * These programs are 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 these programs. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* see documentation in libkmod/libkmod-index.c */
+
+#define INDEX_MAGIC 0xB007F457
+#define INDEX_VERSION_MAJOR 0x0002
+#define INDEX_VERSION_MINOR 0x0001
+#define INDEX_VERSION ((INDEX_VERSION_MAJOR<<16)|INDEX_VERSION_MINOR)
+#define INDEX_CHILDMAX 128
+
+struct index_value {
+ struct index_value *next;
+ unsigned int priority;
+ char value[0];
+};
+
+/* In-memory index (depmod only) */
+struct index_node {
+ char *prefix; /* path compression */
+ struct index_value *values;
+ unsigned char first; /* range of child nodes */
+ unsigned char last;
+ struct index_node *children[INDEX_CHILDMAX]; /* indexed by character */
+};
+
+
+/* Format of node offsets within index file */
+enum node_offset {
+ INDEX_NODE_FLAGS = 0xF0000000, /* Flags in high nibble */
+ INDEX_NODE_PREFIX = 0x80000000,
+ INDEX_NODE_VALUES = 0x40000000,
+ INDEX_NODE_CHILDS = 0x20000000,
+
+ INDEX_NODE_MASK = 0x0FFFFFFF, /* Offset value */
+};
+
+static struct index_node *index_create(void)
+{
+ struct index_node *node;
+
+ node = NOFAIL(calloc(1, sizeof(struct index_node)));
+ node->prefix = NOFAIL(strdup(""));
+ node->first = INDEX_CHILDMAX;
+
+ return node;
+}
+
+static void index_values_free(struct index_value *values)
+{
+ while (values) {
+ struct index_value *value = values;
+
+ values = value->next;
+ free(value);
+ }
+}
+
+static void index_destroy(struct index_node *node)
+{
+ int c;
+
+ for (c = node->first; c <= node->last; c++) {
+ struct index_node *child = node->children[c];
+
+ if (child)
+ index_destroy(child);
+ }
+ index_values_free(node->values);
+ free(node->prefix);
+ free(node);
+}
+
+static void index__checkstring(const char *str)
+{
+ int i;
+
+ for (i = 0; str[i]; i++) {
+ int ch = str[i];
+
+ if (ch >= INDEX_CHILDMAX)
+ CRIT("Module index: bad character '%c'=0x%x - only 7-bit ASCII is supported:"
+ "\n%s\n", (char) ch, (int) ch, str);
+ }
+}
+
+static int index_add_value(struct index_value **values,
+ const char *value, unsigned int priority)
+{
+ struct index_value *v;
+ int duplicate = 0;
+ int len;
+
+ /* report the presence of duplicate values */
+ for (v = *values; v; v = v->next) {
+ if (streq(v->value, value))
+ duplicate = 1;
+ }
+
+ /* find position to insert value */
+ while (*values && (*values)->priority < priority)
+ values = &(*values)->next;
+
+ len = strlen(value);
+ v = NOFAIL(calloc(1, sizeof(struct index_value) + len + 1));
+ v->next = *values;
+ v->priority = priority;
+ memcpy(v->value, value, len + 1);
+ *values = v;
+
+ return duplicate;
+}
+
+static int index_insert(struct index_node *node, const char *key,
+ const char *value, unsigned int priority)
+{
+ int i = 0; /* index within str */
+ int ch;
+
+ index__checkstring(key);
+ index__checkstring(value);
+
+ while(1) {
+ int j; /* index within node->prefix */
+
+ /* Ensure node->prefix is a prefix of &str[i].
+ If it is not already, then we must split node. */
+ for (j = 0; node->prefix[j]; j++) {
+ ch = node->prefix[j];
+
+ if (ch != key[i+j]) {
+ char *prefix = node->prefix;
+ struct index_node *n;
+
+ /* New child is copy of node with prefix[j+1..N] */
+ n = NOFAIL(calloc(1, sizeof(struct index_node)));
+ memcpy(n, node, sizeof(struct index_node));
+ n->prefix = NOFAIL(strdup(&prefix[j+1]));
+
+ /* Parent has prefix[0..j], child at prefix[j] */
+ memset(node, 0, sizeof(struct index_node));
+ prefix[j] = '\0';
+ node->prefix = prefix;
+ node->first = ch;
+ node->last = ch;
+ node->children[ch] = n;
+
+ break;
+ }
+ }
+ /* j is now length of node->prefix */
+ i += j;
+
+ ch = key[i];
+ if(ch == '\0')
+ return index_add_value(&node->values, value, priority);
+
+ if (!node->children[ch]) {
+ struct index_node *child;
+
+ if (ch < node->first)
+ node->first = ch;
+ if (ch > node->last)
+ node->last = ch;
+ node->children[ch] = NOFAIL(calloc(1, sizeof(struct index_node)));
+
+ child = node->children[ch];
+ child->prefix = NOFAIL(strdup(&key[i+1]));
+ child->first = INDEX_CHILDMAX;
+ index_add_value(&child->values, value, priority);
+
+ return 0;
+ }
+
+ /* Descend into child node and continue */
+ node = node->children[ch];
+ i++;
+ }
+}
+
+static int index__haschildren(const struct index_node *node)
+{
+ return node->first < INDEX_CHILDMAX;
+}
+
+/* Recursive post-order traversal
+
+ Pre-order would make for better read-side buffering / readahead / caching.
+ (post-order means you go backwards in the file as you descend the tree).
+ However, index reading is already fast enough.
+ Pre-order is simpler for writing, and depmod is already slow.
+ */
+static uint32_t index_write__node(const struct index_node *node, FILE *out)
+{
+ uint32_t *child_offs = NULL;
+ int child_count = 0;
+ long offset;
+
+ if (!node)
+ return 0;
+
+ /* Write children and save their offsets */
+ if (index__haschildren(node)) {
+ const struct index_node *child;
+ int i;
+
+ child_count = node->last - node->first + 1;
+ child_offs = NOFAIL(malloc(child_count * sizeof(uint32_t)));
+
+ for (i = 0; i < child_count; i++) {
+ child = node->children[node->first + i];
+ child_offs[i] = htonl(index_write__node(child, out));
+ }
+ }
+
+ /* Now write this node */
+ offset = ftell(out);
+
+ if (node->prefix[0]) {
+ fputs(node->prefix, out);
+ fputc('\0', out);
+ offset |= INDEX_NODE_PREFIX;
+ }
+
+ if (child_count) {
+ fputc(node->first, out);
+ fputc(node->last, out);
+ fwrite(child_offs, sizeof(uint32_t), child_count, out);
+ offset |= INDEX_NODE_CHILDS;
+ }
+
+ free(child_offs);
+
+ if (node->values) {
+ const struct index_value *v;
+ unsigned int value_count;
+ uint32_t u;
+
+ value_count = 0;
+ for (v = node->values; v != NULL; v = v->next)
+ value_count++;
+ u = htonl(value_count);
+ fwrite(&u, sizeof(u), 1, out);
+
+ for (v = node->values; v != NULL; v = v->next) {
+ u = htonl(v->priority);
+ fwrite(&u, sizeof(u), 1, out);
+ fputs(v->value, out);
+ fputc('\0', out);
+ }
+ offset |= INDEX_NODE_VALUES;
+ }
+
+ return offset;
+}
+
+static void index_write(const struct index_node *node, FILE *out)
+{
+ long initial_offset, final_offset;
+ uint32_t u;
+
+ u = htonl(INDEX_MAGIC);
+ fwrite(&u, sizeof(u), 1, out);
+ u = htonl(INDEX_VERSION);
+ fwrite(&u, sizeof(u), 1, out);
+
+ /* Second word is reserved for the offset of the root node */
+ initial_offset = ftell(out);
+ assert(initial_offset >= 0);
+ u = 0;
+ fwrite(&u, sizeof(uint32_t), 1, out);
+
+ /* Dump trie */
+ u = htonl(index_write__node(node, out));
+
+ /* Update first word */
+ final_offset = ftell(out);
+ assert(final_offset >= 0);
+ (void)fseek(out, initial_offset, SEEK_SET);
+ fwrite(&u, sizeof(uint32_t), 1, out);
+ (void)fseek(out, final_offset, SEEK_SET);
+}
+
+/* END: code from module-init-tools/index.c just modified to compile here.
+ */
+
+/* configuration parsing **********************************************/
+struct cfg_override {
+ struct cfg_override *next;
+ size_t len;
+ char path[];
+};
+
+enum search_type {
+ SEARCH_PATH,
+ SEARCH_BUILTIN,
+ SEARCH_EXTERNAL
+};
+
+struct cfg_search {
+ struct cfg_search *next;
+ enum search_type type;
+ size_t len;
+ char path[];
+};
+
+struct cfg_external {
+ struct cfg_external *next;
+ size_t len;
+ char path[];
+};
+
+struct cfg_exclude {
+ struct cfg_exclude *next;
+ char exclude_dir[];
+};
+
+struct cfg {
+ const char *kversion;
+ char dirname[PATH_MAX];
+ size_t dirnamelen;
+ char outdirname[PATH_MAX];
+ size_t outdirnamelen;
+ char sym_prefix;
+ uint8_t check_symvers;
+ uint8_t print_unknown;
+ uint8_t warn_dups;
+ struct cfg_override *overrides;
+ struct cfg_search *searches;
+ struct cfg_external *externals;
+ struct cfg_exclude *excludes;
+};
+
+static enum search_type cfg_define_search_type(const char *path)
+{
+ if (streq(path, CFG_BUILTIN_KEY))
+ return SEARCH_BUILTIN;
+ if (streq(path, CFG_EXTERNAL_KEY))
+ return SEARCH_EXTERNAL;
+ return SEARCH_PATH;
+}
+
+static int cfg_search_add(struct cfg *cfg, const char *path)
+{
+ struct cfg_search *s;
+ size_t len;
+ enum search_type type;
+
+ type = cfg_define_search_type(path);
+
+ if (type != SEARCH_PATH)
+ len = 0;
+ else
+ len = strlen(path) + 1;
+
+ s = malloc(sizeof(struct cfg_search) + len);
+ if (s == NULL) {
+ ERR("search add: out of memory\n");
+ return -ENOMEM;
+ }
+ s->type = type;
+ if (type != SEARCH_PATH)
+ s->len = 0;
+ else {
+ s->len = len - 1;
+ memcpy(s->path, path, len);
+ }
+
+ DBG("search add: %s, search type=%hhu\n", path, type);
+
+ s->next = cfg->searches;
+ cfg->searches = s;
+ return 0;
+}
+
+static void cfg_search_free(struct cfg_search *s)
+{
+ free(s);
+}
+
+static int cfg_override_add(struct cfg *cfg, const char *modname, const char *subdir)
+{
+ struct cfg_override *o;
+ size_t modnamelen = strlen(modname);
+ size_t subdirlen = strlen(subdir);
+ size_t i;
+
+ o = malloc(sizeof(struct cfg_override) + subdirlen + 1
+ + modnamelen + 1);
+ if (o == NULL) {
+ ERR("override add: out of memory\n");
+ return -ENOMEM;
+ }
+ memcpy(o->path, subdir, subdirlen);
+ i = subdirlen;
+ o->path[i] = '/';
+ i++;
+
+ memcpy(o->path + i, modname, modnamelen);
+ i += modnamelen;
+ o->path[i] = '\0'; /* no extension, so we can match .ko/.ko.gz */
+
+ o->len = i;
+
+ DBG("override add: %s\n", o->path);
+
+ o->next = cfg->overrides;
+ cfg->overrides = o;
+ return 0;
+}
+
+static void cfg_override_free(struct cfg_override *o)
+{
+ free(o);
+}
+
+static int cfg_external_add(struct cfg *cfg, const char *path)
+{
+ struct cfg_external *ext;
+ size_t len = strlen(path);
+
+ ext = malloc(sizeof(struct cfg_external) + len + 1);
+ if (ext == NULL) {
+ ERR("external add: out of memory\n");
+ return -ENOMEM;
+ }
+
+ strcpy(ext->path, path);
+ ext->len = len;
+
+ DBG("external add: %s\n", ext->path);
+
+ ext->next = cfg->externals;
+ cfg->externals = ext;
+ return 0;
+}
+
+static void cfg_external_free(struct cfg_external *ext)
+{
+ free(ext);
+}
+
+static int cfg_exclude_add(struct cfg *cfg, const char *path)
+{
+ struct cfg_exclude *exc;
+ size_t len = strlen(path);
+
+ exc = malloc(sizeof(struct cfg_exclude) + len + 1);
+ if (exc == NULL) {
+ ERR("exclude add: out of memory\n");
+ return -ENOMEM;
+ }
+ memcpy(exc->exclude_dir, path, len + 1);
+
+ DBG("exclude add: %s\n", path);
+
+ exc->next = cfg->excludes;
+ cfg->excludes = exc;
+ return 0;
+}
+
+static void cfg_exclude_free(struct cfg_exclude *exc)
+{
+ free(exc);
+}
+
+static int cfg_kernel_matches(const struct cfg *cfg, const char *pattern)
+{
+ regex_t re;
+ int status;
+
+ /* old style */
+ if (streq(pattern, "*"))
+ return 1;
+
+ if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0)
+ return 0;
+
+ status = regexec(&re, cfg->kversion, 0, NULL, 0);
+ regfree(&re);
+
+ return status == 0;
+}
+
+static int cfg_file_parse(struct cfg *cfg, const char *filename)
+{
+ char *line;
+ FILE *fp;
+ unsigned int linenum = 0;
+ int err;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ err = -errno;
+ ERR("file parse %s: %m\n", filename);
+ return err;
+ }
+
+ while ((line = freadline_wrapped(fp, &linenum)) != NULL) {
+ char *cmd, *saveptr;
+
+ if (line[0] == '\0' || line[0] == '#')
+ goto done_next;
+
+ cmd = strtok_r(line, "\t ", &saveptr);
+ if (cmd == NULL)
+ goto done_next;
+
+ if (streq(cmd, "search")) {
+ const char *sp;
+ while ((sp = strtok_r(NULL, "\t ", &saveptr)) != NULL) {
+ cfg_search_add(cfg, sp);
+ }
+ } else if (streq(cmd, "override")) {
+ const char *modname = strtok_r(NULL, "\t ", &saveptr);
+ const char *version = strtok_r(NULL, "\t ", &saveptr);
+ const char *subdir = strtok_r(NULL, "\t ", &saveptr);
+
+ if (modname == NULL || version == NULL ||
+ subdir == NULL)
+ goto syntax_error;
+
+ if (!cfg_kernel_matches(cfg, version)) {
+ INF("%s:%u: override kernel did not match %s\n",
+ filename, linenum, version);
+ goto done_next;
+ }
+
+ cfg_override_add(cfg, modname, subdir);
+ } else if (streq(cmd, "external")) {
+ const char *version = strtok_r(NULL, "\t ", &saveptr);
+ const char *dir = strtok_r(NULL, "\t ", &saveptr);
+
+ if (version == NULL || dir == NULL)
+ goto syntax_error;
+
+ if (!cfg_kernel_matches(cfg, version)) {
+ INF("%s:%u: external directory did not match %s\n",
+ filename, linenum, version);
+ goto done_next;
+ }
+
+ cfg_external_add(cfg, dir);
+ } else if (streq(cmd, "exclude")) {
+ const char *sp;
+ while ((sp = strtok_r(NULL, "\t ", &saveptr)) != NULL) {
+ cfg_exclude_add(cfg, sp);
+ }
+ } else if (streq(cmd, "include")
+ || streq(cmd, "make_map_files")) {
+ INF("%s:%u: command %s not implemented yet\n",
+ filename, linenum, cmd);
+ } else {
+syntax_error:
+ ERR("%s:%u: ignoring bad line starting with '%s'\n",
+ filename, linenum, cmd);
+ }
+
+done_next:
+ free(line);
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+static int cfg_files_filter_out(DIR *d, const char *dir, const char *name)
+{
+ size_t len = strlen(name);
+ struct stat st;
+
+ if (name[0] == '.')
+ return 1;
+
+ if (len < 6 || !streq(name + len - 5, ".conf")) {
+ INF("All cfg files need .conf: %s/%s\n", dir, name);
+ return 1;
+ }
+
+ fstatat(dirfd(d), name, &st, 0);
+ if (S_ISDIR(st.st_mode)) {
+ ERR("Directories inside directories are not supported: %s/%s\n",
+ dir, name);
+ return 1;
+ }
+
+ return 0;
+}
+
+struct cfg_file {
+ size_t dirlen;
+ size_t namelen;
+ const char *name;
+ char path[];
+};
+
+static void cfg_file_free(struct cfg_file *f)
+{
+ free(f);
+}
+
+static int cfg_files_insert_sorted(struct cfg_file ***p_files, size_t *p_n_files,
+ const char *dir, const char *name)
+{
+ struct cfg_file **files, *f;
+ size_t i, n_files, namelen, dirlen;
+ void *tmp;
+
+ dirlen = strlen(dir);
+ if (name != NULL)
+ namelen = strlen(name);
+ else {
+ name = basename(dir);
+ namelen = strlen(name);
+ dirlen -= namelen + 1;
+ }
+
+ n_files = *p_n_files;
+ files = *p_files;
+ for (i = 0; i < n_files; i++) {
+ int cmp = strcmp(name, files[i]->name);
+ if (cmp == 0) {
+ DBG("Ignoring duplicate config file: %.*s/%s\n",
+ (int)dirlen, dir, name);
+ return -EEXIST;
+ } else if (cmp < 0)
+ break;
+ }
+
+ f = malloc(sizeof(struct cfg_file) + dirlen + namelen + 2);
+ if (f == NULL) {
+ ERR("files insert sorted: out of memory\n");
+ return -ENOMEM;
+ }
+
+ tmp = realloc(files, sizeof(struct cfg_file *) * (n_files + 1));
+ if (tmp == NULL) {
+ ERR("files insert sorted: out of memory\n");
+ free(f);
+ return -ENOMEM;
+ }
+ *p_files = files = tmp;
+
+ if (i < n_files) {
+ memmove(files + i + 1, files + i,
+ sizeof(struct cfg_file *) * (n_files - i));
+ }
+ files[i] = f;
+
+ f->dirlen = dirlen;
+ f->namelen = namelen;
+ f->name = f->path + dirlen + 1;
+ memcpy(f->path, dir, dirlen);
+ f->path[dirlen] = '/';
+ memcpy(f->path + dirlen + 1, name, namelen);
+ f->path[dirlen + 1 + namelen] = '\0';
+
+ *p_n_files = n_files + 1;
+ return 0;
+}
+
+/*
+ * Insert configuration files ignoring duplicates
+ */
+static int cfg_files_list(struct cfg_file ***p_files, size_t *p_n_files,
+ const char *path)
+{
+ struct dirent *dent;
+ DIR *d;
+ int err = 0;
+ struct stat st;
+
+ if (stat(path, &st) != 0) {
+ err = -errno;
+ DBG("could not stat '%s': %m\n", path);
+ return err;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ cfg_files_insert_sorted(p_files, p_n_files, path, NULL);
+ return 0;
+ }
+
+ d = opendir(path);
+ if (d == NULL) {
+ ERR("files list %s: %m\n", path);
+ return -EINVAL;
+ }
+
+ for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
+ if (cfg_files_filter_out(d, path, dent->d_name))
+ continue;
+
+ cfg_files_insert_sorted(p_files, p_n_files, path, dent->d_name);
+ }
+
+ closedir(d);
+ DBG("parsed configuration files from %s\n", path);
+ return err;
+}
+
+static int cfg_load(struct cfg *cfg, const char * const *cfg_paths)
+{
+ size_t i, n_files = 0;
+ struct cfg_file **files = NULL;
+
+ if (cfg_paths == NULL)
+ cfg_paths = default_cfg_paths;
+
+ for (i = 0; cfg_paths[i] != NULL; i++)
+ cfg_files_list(&files, &n_files, cfg_paths[i]);
+
+ for (i = 0; i < n_files; i++) {
+ struct cfg_file *f = files[i];
+ cfg_file_parse(cfg, f->path);
+ cfg_file_free(f);
+ }
+ free(files);
+
+ /* For backward compatibility add "updates" to the head of the search
+ * list here. But only if there was no "search" option specified.
+ */
+ if (cfg->searches == NULL)
+ cfg_search_add(cfg, "updates");
+
+ return 0;
+}
+
+static void cfg_free(struct cfg *cfg)
+{
+ while (cfg->overrides) {
+ struct cfg_override *tmp = cfg->overrides;
+ cfg->overrides = cfg->overrides->next;
+ cfg_override_free(tmp);
+ }
+
+ while (cfg->searches) {
+ struct cfg_search *tmp = cfg->searches;
+ cfg->searches = cfg->searches->next;
+ cfg_search_free(tmp);
+ }
+
+ while (cfg->externals) {
+ struct cfg_external *tmp = cfg->externals;
+ cfg->externals = cfg->externals->next;
+ cfg_external_free(tmp);
+ }
+
+ while (cfg->excludes) {
+ struct cfg_exclude *tmp = cfg->excludes;
+ cfg->excludes = cfg->excludes->next;
+ cfg_exclude_free(tmp);
+ }
+}
+
+
+/* depmod calculations ***********************************************/
+struct vertex;
+struct mod {
+ struct kmod_module *kmod;
+ char *path;
+ const char *relpath; /* path relative to '$ROOT$MODULE_DIRECTORY/$VER/' */
+ char *uncrelpath; /* same as relpath but ending in .ko */
+ struct kmod_list *info_list;
+ struct kmod_list *dep_sym_list;
+ struct array deps; /* struct symbol */
+ size_t baselen; /* points to start of basename/filename */
+ size_t modnamesz;
+ int sort_idx; /* sort index using modules.order */
+ int dep_sort_idx; /* topological sort index */
+ uint16_t idx; /* index in depmod->modules.array */
+ uint16_t users; /* how many modules depend on this one */
+ bool visited; /* helper field to report cycles */
+ struct vertex *vertex; /* helper field to report cycles */
+ char modname[];
+};
+
+struct symbol {
+ struct mod *owner;
+ uint64_t crc;
+ char name[];
+};
+
+struct depmod {
+ const struct cfg *cfg;
+ struct kmod_ctx *ctx;
+ struct array modules;
+ struct hash *modules_by_uncrelpath;
+ struct hash *modules_by_name;
+ struct hash *symbols;
+};
+
+static void mod_free(struct mod *mod)
+{
+ DBG("free %p kmod=%p, path=%s\n", mod, mod->kmod, mod->path);
+ array_free_array(&mod->deps);
+ kmod_module_unref(mod->kmod);
+ kmod_module_info_free_list(mod->info_list);
+ kmod_module_dependency_symbols_free_list(mod->dep_sym_list);
+ free(mod->uncrelpath);
+ free(mod->path);
+ free(mod);
+}
+
+static int mod_add_dependency(struct mod *mod, struct symbol *sym)
+{
+ int err;
+
+ DBG("%s depends on %s %s\n", mod->path, sym->name,
+ sym->owner != NULL ? sym->owner->path : "(unknown)");
+
+ if (sym->owner == NULL)
+ return 0;
+
+ err = array_append_unique(&mod->deps, sym->owner);
+ if (err == -EEXIST)
+ return 0;
+ if (err < 0)
+ return err;
+
+ sym->owner->users++;
+ SHOW("%s needs \"%s\": %s\n", mod->path, sym->name, sym->owner->path);
+ return 0;
+}
+
+static void symbol_free(struct symbol *sym)
+{
+ DBG("free %p sym=%s, owner=%p %s\n", sym, sym->name, sym->owner,
+ sym->owner != NULL ? sym->owner->path : "");
+ free(sym);
+}
+
+static int depmod_init(struct depmod *depmod, struct cfg *cfg,
+ struct kmod_ctx *ctx)
+{
+ int err = 0;
+
+ depmod->cfg = cfg;
+ depmod->ctx = ctx;
+
+ array_init(&depmod->modules, 128);
+
+ depmod->modules_by_uncrelpath = hash_new(512, NULL);
+ if (depmod->modules_by_uncrelpath == NULL) {
+ err = -errno;
+ goto modules_by_uncrelpath_failed;
+ }
+
+ depmod->modules_by_name = hash_new(512, NULL);
+ if (depmod->modules_by_name == NULL) {
+ err = -errno;
+ goto modules_by_name_failed;
+ }
+
+ depmod->symbols = hash_new(2048, (void (*)(void *))symbol_free);
+ if (depmod->symbols == NULL) {
+ err = -errno;
+ goto symbols_failed;
+ }
+
+ return 0;
+
+symbols_failed:
+ hash_free(depmod->modules_by_name);
+modules_by_name_failed:
+ hash_free(depmod->modules_by_uncrelpath);
+modules_by_uncrelpath_failed:
+ return err;
+}
+
+static void depmod_shutdown(struct depmod *depmod)
+{
+ size_t i;
+
+ hash_free(depmod->symbols);
+
+ hash_free(depmod->modules_by_uncrelpath);
+
+ hash_free(depmod->modules_by_name);
+
+ for (i = 0; i < depmod->modules.count; i++)
+ mod_free(depmod->modules.array[i]);
+ array_free_array(&depmod->modules);
+
+ kmod_unref(depmod->ctx);
+}
+
+static int depmod_module_add(struct depmod *depmod, struct kmod_module *kmod)
+{
+ const struct cfg *cfg = depmod->cfg;
+ const char *modname, *lastslash;
+ size_t modnamesz;
+ struct mod *mod;
+ int err;
+
+ modname = kmod_module_get_name(kmod);
+ modnamesz = strlen(modname) + 1;
+
+ mod = calloc(1, sizeof(struct mod) + modnamesz);
+ if (mod == NULL)
+ return -ENOMEM;
+ mod->kmod = kmod;
+ mod->sort_idx = depmod->modules.count + 1;
+ mod->dep_sort_idx = INT32_MAX;
+ memcpy(mod->modname, modname, modnamesz);
+ mod->modnamesz = modnamesz;
+
+ array_init(&mod->deps, 4);
+
+ mod->path = strdup(kmod_module_get_path(kmod));
+ lastslash = strrchr(mod->path, '/');
+ mod->baselen = lastslash - mod->path;
+ if (strncmp(mod->path, cfg->dirname, cfg->dirnamelen) == 0 &&
+ mod->path[cfg->dirnamelen] == '/')
+ mod->relpath = mod->path + cfg->dirnamelen + 1;
+ else
+ mod->relpath = NULL;
+
+ err = hash_add_unique(depmod->modules_by_name, mod->modname, mod);
+ if (err < 0) {
+ ERR("hash_add_unique %s: %s\n", mod->modname, strerror(-err));
+ goto fail;
+ }
+
+ if (mod->relpath != NULL) {
+ size_t uncrelpathlen = lastslash - mod->relpath + modnamesz
+ + strlen(KMOD_EXTENSION_UNCOMPRESSED);
+ mod->uncrelpath = memdup(mod->relpath, uncrelpathlen + 1);
+ mod->uncrelpath[uncrelpathlen] = '\0';
+ err = hash_add_unique(depmod->modules_by_uncrelpath,
+ mod->uncrelpath, mod);
+ if (err < 0) {
+ ERR("hash_add_unique %s: %s\n",
+ mod->uncrelpath, strerror(-err));
+ hash_del(depmod->modules_by_name, mod->modname);
+ goto fail;
+ }
+ }
+
+ DBG("add %p kmod=%p, path=%s\n", mod, kmod, mod->path);
+
+ return 0;
+
+fail:
+ free(mod->uncrelpath);
+ free(mod);
+ return err;
+}
+
+static int depmod_module_del(struct depmod *depmod, struct mod *mod)
+{
+ DBG("del %p kmod=%p, path=%s\n", mod, mod->kmod, mod->path);
+
+ if (mod->uncrelpath != NULL)
+ hash_del(depmod->modules_by_uncrelpath, mod->uncrelpath);
+
+ hash_del(depmod->modules_by_name, mod->modname);
+
+ mod_free(mod);
+ return 0;
+}
+
+static const char *search_to_string(const struct cfg_search *s)
+{
+ switch(s->type) {
+ case SEARCH_EXTERNAL:
+ return "external";
+ case SEARCH_BUILTIN:
+ return "built-in";
+ default:
+ return s->path;
+ }
+}
+
+static bool depmod_is_path_starts_with(const char *path,
+ size_t pathlen,
+ const char *prefix,
+ size_t prefix_len)
+{
+ if (pathlen <= prefix_len)
+ return false;
+ if (path[prefix_len] != '/')
+ return false;
+ if (memcmp(path, prefix, prefix_len) != 0)
+ return false;
+
+ return true;
+}
+
+/* returns if existing module @mod is higher priority than newpath.
+ * note this is the inverse of module-init-tools is_higher_priority()
+ */
+static int depmod_module_is_higher_priority(const struct depmod *depmod, const struct mod *mod, size_t baselen, size_t namelen, size_t modnamelen, const char *newpath)
+{
+ const struct cfg *cfg = depmod->cfg;
+ const struct cfg_override *ov;
+ const struct cfg_search *se;
+ const struct cfg_external *ext;
+
+ /* baselen includes the last '/' and mod->baselen doesn't. So it's
+ * actually correct to use modnamelen in the first and modnamesz in
+ * the latter */
+ size_t newlen = baselen + modnamelen;
+ size_t oldlen = mod->baselen + mod->modnamesz;
+ const char *oldpath = mod->path;
+ int i, bprio = -1, oldprio = -1, newprio = -1;
+ size_t relnewlen = 0;
+ size_t reloldlen = 0;
+ const char *relnewpath = NULL;
+ const char *reloldpath = NULL;
+
+ DBG("comparing priorities of %s and %s\n",
+ oldpath, newpath);
+
+ if (strncmp(newpath, cfg->dirname, cfg->dirnamelen) == 0) {
+ relnewpath = newpath + cfg->dirnamelen + 1;
+ relnewlen = newlen - (cfg->dirnamelen + 1);
+ }
+ if (strncmp(oldpath, cfg->dirname, cfg->dirnamelen) == 0) {
+ reloldpath = oldpath + cfg->dirnamelen + 1;
+ reloldlen = oldlen - (cfg->dirnamelen + 1);
+ }
+
+ for (ov = cfg->overrides; ov != NULL; ov = ov->next) {
+ DBG("override %s\n", ov->path);
+ if (relnewlen == ov->len &&
+ memcmp(ov->path, relnewpath, relnewlen) == 0)
+ return 0;
+ if (reloldlen == ov->len &&
+ memcmp(ov->path, reloldpath, reloldlen) == 0)
+ return 1;
+ }
+
+ for (i = 0, se = cfg->searches; se != NULL; se = se->next, i++) {
+ DBG("search %s\n", search_to_string(se));
+ if (se->type == SEARCH_BUILTIN)
+ bprio = i;
+ else if (se->type == SEARCH_EXTERNAL) {
+ for (ext = cfg->externals; ext != NULL; ext = ext->next, i++) {
+ if (depmod_is_path_starts_with(newpath,
+ newlen,
+ ext->path,
+ ext->len))
+ newprio = i;
+ if (depmod_is_path_starts_with(oldpath,
+ oldlen,
+ ext->path,
+ ext->len))
+ oldprio = i;
+ }
+ } else if (relnewlen > se->len && relnewpath[se->len] == '/' &&
+ memcmp(se->path, relnewpath, se->len) == 0)
+ newprio = i;
+ else if (reloldlen > se->len && reloldpath[se->len] == '/' &&
+ memcmp(se->path, reloldpath, se->len) == 0)
+ oldprio = i;
+ }
+
+ if (newprio < 0)
+ newprio = bprio;
+ if (oldprio < 0)
+ oldprio = bprio;
+
+ DBG("priorities: built-in: %d, old: %d, new: %d\n",
+ bprio, oldprio, newprio);
+
+ return newprio <= oldprio;
+}
+
+static int depmod_modules_search_file(struct depmod *depmod, size_t baselen, size_t namelen, const char *path)
+{
+ struct kmod_module *kmod;
+ struct mod *mod;
+ const char *relpath;
+ char modname[PATH_MAX];
+ size_t modnamelen;
+ int err;
+
+ if (!path_ends_with_kmod_ext(path + baselen, namelen))
+ return 0;
+
+ if (path_to_modname(path, modname, &modnamelen) == NULL) {
+ ERR("could not get modname from path %s\n", path);
+ return -EINVAL;
+ }
+
+ relpath = path + depmod->cfg->dirnamelen + 1;
+ DBG("try %s (%s)\n", relpath, modname);
+
+ mod = hash_find(depmod->modules_by_name, modname);
+ if (mod == NULL)
+ goto add;
+
+ if (depmod_module_is_higher_priority(depmod, mod, baselen,
+ namelen, modnamelen, path)) {
+ DBG("Ignored lower priority: %s, higher: %s\n",
+ path, mod->path);
+ return 0;
+ }
+
+ DBG("Replace lower priority %s with new module %s\n",
+ mod->relpath, relpath);
+ err = depmod_module_del(depmod, mod);
+ if (err < 0) {
+ ERR("could not del module %s: %s\n", mod->path, strerror(-err));
+ return err;
+ }
+
+add:
+ err = kmod_module_new_from_path(depmod->ctx, path, &kmod);
+ if (err < 0) {
+ ERR("could not create module %s: %s\n", path, strerror(-err));
+ return err;
+ }
+
+ err = depmod_module_add(depmod, kmod);
+ if (err < 0) {
+ ERR("could not add module %s: %s\n",
+ path, strerror(-err));
+ kmod_module_unref(kmod);
+ return err;
+ }
+ return 0;
+}
+
+static bool should_exclude_dir(const struct cfg *cfg, const char *name)
+{
+ struct cfg_exclude *exc;
+
+ if (name[0] == '.' && (name[1] == '\0' ||
+ (name[1] == '.' && name[2] == '\0')))
+ return true;
+
+ if (streq(name, "build") || streq(name, "source"))
+ return true;
+
+ for (exc = cfg->excludes; exc != NULL; exc = exc->next) {
+ if (streq(name, exc->exclude_dir))
+ return true;
+ }
+
+ return false;
+}
+
+static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t baselen, struct scratchbuf *s_path)
+{
+ struct dirent *de;
+ int err = 0, dfd = dirfd(d);
+ char *path;
+
+ while ((de = readdir(d)) != NULL) {
+ const char *name = de->d_name;
+ size_t namelen;
+ uint8_t is_dir;
+
+ if (should_exclude_dir(depmod->cfg, name))
+ continue;
+
+ namelen = strlen(name);
+ if (scratchbuf_alloc(s_path, baselen + namelen + 2) < 0) {
+ err = -ENOMEM;
+ ERR("No memory\n");
+ continue;
+ }
+
+ path = scratchbuf_str(s_path);
+ memcpy(path + baselen, name, namelen + 1);
+
+ if (de->d_type == DT_REG)
+ is_dir = 0;
+ else if (de->d_type == DT_DIR)
+ is_dir = 1;
+ else {
+ struct stat st;
+ if (fstatat(dfd, name, &st, 0) < 0) {
+ ERR("fstatat(%d, %s): %m\n", dfd, name);
+ continue;
+ } else if (S_ISREG(st.st_mode))
+ is_dir = 0;
+ else if (S_ISDIR(st.st_mode))
+ is_dir = 1;
+ else {
+ ERR("unsupported file type %s: %o\n",
+ path, st.st_mode & S_IFMT);
+ continue;
+ }
+ }
+
+ if (is_dir) {
+ int fd;
+ DIR *subdir;
+ fd = openat(dfd, name, O_RDONLY);
+ if (fd < 0) {
+ ERR("openat(%d, %s, O_RDONLY): %m\n",
+ dfd, name);
+ continue;
+ }
+ subdir = fdopendir(fd);
+ if (subdir == NULL) {
+ ERR("fdopendir(%d): %m\n", fd);
+ close(fd);
+ continue;
+ }
+ path[baselen + namelen] = '/';
+ path[baselen + namelen + 1] = '\0';
+ err = depmod_modules_search_dir(depmod, subdir,
+ baselen + namelen + 1,
+ s_path);
+ closedir(subdir);
+ } else {
+ err = depmod_modules_search_file(depmod, baselen,
+ namelen, path);
+ }
+
+ if (err < 0) {
+ path[baselen + namelen] = '\0';
+ ERR("failed %s: %s\n", path, strerror(-err));
+ err = 0; /* ignore errors */
+ }
+ }
+ return err;
+}
+
+static int depmod_modules_search_path(struct depmod *depmod,
+ const char *path)
+{
+ char buf[256];
+ _cleanup_(scratchbuf_release) struct scratchbuf s_path_buf =
+ SCRATCHBUF_INITIALIZER(buf);
+ char *path_buf;
+ DIR *d;
+ size_t baselen;
+ int err;
+
+ d = opendir(path);
+ if (d == NULL) {
+ err = -errno;
+ ERR("could not open directory %s: %m\n", path);
+ return err;
+ }
+
+ baselen = strlen(path);
+
+ if (scratchbuf_alloc(&s_path_buf, baselen + 2) < 0) {
+ err = -ENOMEM;
+ goto out;
+ }
+ path_buf = scratchbuf_str(&s_path_buf);
+
+ memcpy(path_buf, path, baselen);
+ path_buf[baselen] = '/';
+ baselen++;
+ path_buf[baselen] = '\0';
+
+ err = depmod_modules_search_dir(depmod, d, baselen, &s_path_buf);
+out:
+ closedir(d);
+ return err;
+}
+
+static int depmod_modules_search(struct depmod *depmod)
+{
+ int err;
+ struct cfg_external *ext;
+
+ err = depmod_modules_search_path(depmod, depmod->cfg->dirname);
+ if (err < 0)
+ return err;
+
+ for (ext = depmod->cfg->externals; ext != NULL; ext = ext->next) {
+ err = depmod_modules_search_path(depmod, ext->path);
+ if (err < 0 && err == -ENOENT)
+ /* ignore external dir absense */
+ continue;
+ }
+
+ return 0;
+}
+
+static int mod_cmp(const void *pa, const void *pb) {
+ const struct mod *a = *(const struct mod **)pa;
+ const struct mod *b = *(const struct mod **)pb;
+ return a->sort_idx - b->sort_idx;
+}
+
+static int depmod_modules_build_array(struct depmod *depmod)
+{
+ struct hash_iter module_iter;
+ const void *v;
+ int err;
+
+ hash_iter_init(depmod->modules_by_name, &module_iter);
+ while (hash_iter_next(&module_iter, NULL, &v)) {
+ struct mod *mod = (struct mod *) v;
+ mod->idx = depmod->modules.count;
+ err = array_append(&depmod->modules, mod);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static FILE *dfdopen(const char *dname, const char *filename, int flags,
+ const char *mode)
+{
+ int fd, dfd;
+ FILE *ret;
+
+ dfd = open(dname, O_RDONLY);
+ if (dfd < 0) {
+ WRN("could not open directory %s: %m\n", dname);
+ return NULL;
+ }
+
+ fd = openat(dfd, filename, flags);
+ if (fd < 0) {
+ WRN("could not open %s at %s: %m\n", filename, dname);
+ ret = NULL;
+ } else {
+ ret = fdopen(fd, mode);
+ if (!ret) {
+ WRN("could not associate stream with %s: %m\n", filename);
+ close(fd);
+ }
+ }
+ close(dfd);
+ return ret;
+}
+
+
+
+static void depmod_modules_sort(struct depmod *depmod)
+{
+ char line[PATH_MAX];
+ const char *order_file = "modules.order";
+ FILE *fp;
+ unsigned idx = 0, total = 0;
+
+ fp = dfdopen(depmod->cfg->dirname, order_file, O_RDONLY, "r");
+ if (fp == NULL)
+ return;
+
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ size_t len = strlen(line);
+ idx++;
+ if (len == 0)
+ continue;
+ if (line[len - 1] != '\n') {
+ ERR("%s/%s:%u corrupted line misses '\\n'\n",
+ depmod->cfg->dirname, order_file, idx);
+ goto corrupted;
+ }
+ }
+ total = idx + 1;
+ idx = 0;
+ fseek(fp, 0, SEEK_SET);
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ size_t len = strlen(line);
+ struct mod *mod;
+
+ idx++;
+ if (len == 0)
+ continue;
+ line[len - 1] = '\0';
+
+ mod = hash_find(depmod->modules_by_uncrelpath, line);
+ if (mod == NULL)
+ continue;
+ mod->sort_idx = idx - total;
+ }
+
+ array_sort(&depmod->modules, mod_cmp);
+ for (idx = 0; idx < depmod->modules.count; idx++) {
+ struct mod *m = depmod->modules.array[idx];
+ m->idx = idx;
+ }
+
+corrupted:
+ fclose(fp);
+}
+
+static int depmod_symbol_add(struct depmod *depmod, const char *name,
+ bool prefix_skipped, uint64_t crc,
+ const struct mod *owner)
+{
+ size_t namelen;
+ int err;
+ struct symbol *sym;
+
+ if (!prefix_skipped && (name[0] == depmod->cfg->sym_prefix))
+ name++;
+
+ namelen = strlen(name) + 1;
+ sym = malloc(sizeof(struct symbol) + namelen);
+ if (sym == NULL)
+ return -ENOMEM;
+
+ sym->owner = (struct mod *)owner;
+ sym->crc = crc;
+ memcpy(sym->name, name, namelen);
+
+ err = hash_add(depmod->symbols, sym->name, sym);
+ if (err < 0) {
+ free(sym);
+ return err;
+ }
+
+ DBG("add %p sym=%s, owner=%p %s\n", sym, sym->name, owner,
+ owner != NULL ? owner->path : "");
+
+ return 0;
+}
+
+static struct symbol *depmod_symbol_find(const struct depmod *depmod,
+ const char *name)
+{
+ if (name[0] == '.') /* PPC64 needs this: .foo == foo */
+ name++;
+ if (name[0] == depmod->cfg->sym_prefix)
+ name++;
+ return hash_find(depmod->symbols, name);
+}
+
+static int depmod_load_modules(struct depmod *depmod)
+{
+ struct mod **itr, **itr_end;
+
+ DBG("load symbols (%zd modules)\n", depmod->modules.count);
+
+ itr = (struct mod **)depmod->modules.array;
+ itr_end = itr + depmod->modules.count;
+ for (; itr < itr_end; itr++) {
+ struct mod *mod = *itr;
+ struct kmod_list *l, *list = NULL;
+ int err = kmod_module_get_symbols(mod->kmod, &list);
+ if (err < 0) {
+ if (err == -ENODATA)
+ DBG("ignoring %s: no symbols\n", mod->path);
+ else
+ ERR("failed to load symbols from %s: %s\n",
+ mod->path, strerror(-err));
+ goto load_info;
+ }
+ kmod_list_foreach(l, list) {
+ const char *name = kmod_module_symbol_get_symbol(l);
+ uint64_t crc = kmod_module_symbol_get_crc(l);
+ depmod_symbol_add(depmod, name, false, crc, mod);
+ }
+ kmod_module_symbols_free_list(list);
+
+load_info:
+ kmod_module_get_info(mod->kmod, &mod->info_list);
+ kmod_module_get_dependency_symbols(mod->kmod,
+ &mod->dep_sym_list);
+ kmod_module_unref(mod->kmod);
+ mod->kmod = NULL;
+ }
+
+ DBG("loaded symbols (%zd modules, %u symbols)\n",
+ depmod->modules.count, hash_get_count(depmod->symbols));
+
+ return 0;
+}
+
+static int depmod_load_module_dependencies(struct depmod *depmod, struct mod *mod)
+{
+ const struct cfg *cfg = depmod->cfg;
+ struct kmod_list *l;
+
+ DBG("do dependencies of %s\n", mod->path);
+ kmod_list_foreach(l, mod->dep_sym_list) {
+ const char *name = kmod_module_dependency_symbol_get_symbol(l);
+ uint64_t crc = kmod_module_dependency_symbol_get_crc(l);
+ int bindtype = kmod_module_dependency_symbol_get_bind(l);
+ struct symbol *sym = depmod_symbol_find(depmod, name);
+ uint8_t is_weak = bindtype == KMOD_SYMBOL_WEAK;
+
+ if (sym == NULL) {
+ DBG("%s needs (%c) unknown symbol %s\n",
+ mod->path, bindtype, name);
+ if (cfg->print_unknown && !is_weak)
+ WRN("%s needs unknown symbol %s\n",
+ mod->path, name);
+ continue;
+ }
+
+ if (cfg->check_symvers && sym->crc != crc && !is_weak) {
+ DBG("symbol %s (%#"PRIx64") module %s (%#"PRIx64")\n",
+ sym->name, sym->crc, mod->path, crc);
+ if (cfg->print_unknown)
+ WRN("%s disagrees about version of symbol %s\n",
+ mod->path, name);
+ }
+
+ mod_add_dependency(mod, sym);
+ }
+
+ return 0;
+}
+
+static int depmod_load_dependencies(struct depmod *depmod)
+{
+ struct mod **itr, **itr_end;
+
+ DBG("load dependencies (%zd modules, %u symbols)\n",
+ depmod->modules.count, hash_get_count(depmod->symbols));
+
+ itr = (struct mod **)depmod->modules.array;
+ itr_end = itr + depmod->modules.count;
+ for (; itr < itr_end; itr++) {
+ struct mod *mod = *itr;
+
+ if (mod->dep_sym_list == NULL) {
+ DBG("ignoring %s: no dependency symbols\n", mod->path);
+ continue;
+ }
+
+ depmod_load_module_dependencies(depmod, mod);
+ }
+
+ DBG("loaded dependencies (%zd modules, %u symbols)\n",
+ depmod->modules.count, hash_get_count(depmod->symbols));
+
+ return 0;
+}
+
+static int dep_cmp(const void *pa, const void *pb)
+{
+ const struct mod *a = *(const struct mod **)pa;
+ const struct mod *b = *(const struct mod **)pb;
+ return a->dep_sort_idx - b->dep_sort_idx;
+}
+
+static void depmod_sort_dependencies(struct depmod *depmod)
+{
+ struct mod **itr, **itr_end;
+ itr = (struct mod **)depmod->modules.array;
+ itr_end = itr + depmod->modules.count;
+ for (; itr < itr_end; itr++) {
+ struct mod *m = *itr;
+ if (m->deps.count > 1)
+ array_sort(&m->deps, dep_cmp);
+ }
+}
+
+struct vertex {
+ struct vertex *parent;
+ struct mod *mod;
+};
+
+static struct vertex *vertex_new(struct mod *mod, struct vertex *parent)
+{
+ struct vertex *v;
+
+ v = malloc(sizeof(*v));
+ if (v == NULL)
+ return NULL;
+
+ v->parent = parent;
+ v->mod = mod;
+ return v;
+}
+
+static void depmod_list_remove_data(struct kmod_list **list, void *data)
+{
+ struct kmod_list *l;
+
+ l = kmod_list_remove_data(*list, data);
+ *list = l;
+}
+
+static int depmod_report_one_cycle(struct depmod *depmod,
+ struct vertex *vertex,
+ struct kmod_list **roots,
+ struct hash *loop_set)
+{
+ const char sep[] = " -> ";
+ size_t sz;
+ char *buf;
+ struct array reverse;
+ int i;
+ int n;
+ struct vertex *v;
+ int rc;
+
+ array_init(&reverse, 3);
+
+ sz = 0;
+ for (v = vertex->parent, n = 0;
+ v != NULL;
+ v = v->parent, n++) {
+
+ sz += v->mod->modnamesz - 1;
+ array_append(&reverse, v);
+ rc = hash_add(loop_set, v->mod->modname, NULL);
+ if (rc != 0)
+ return rc;
+ /* the hash will be freed where created */
+ }
+ sz += vertex->mod->modnamesz - 1;
+
+ buf = malloc(sz + n * strlen(sep) + 1);
+
+ sz = 0;
+ for (i = reverse.count - 1; i >= 0; i--) {
+ size_t len;
+
+ v = reverse.array[i];
+
+ len = v->mod->modnamesz - 1;
+ memcpy(buf + sz, v->mod->modname, len);
+ sz += len;
+ strcpy(buf + sz, sep);
+ sz += strlen(sep);
+
+ depmod_list_remove_data(roots, v->mod);
+ }
+ strcpy(buf + sz, vertex->mod->modname);
+ ERR("Cycle detected: %s\n", buf);
+
+ free(buf);
+ array_free_array(&reverse);
+
+ return 0;
+}
+
+static int depmod_report_cycles_from_root(struct depmod *depmod,
+ struct mod *root_mod,
+ struct kmod_list **roots,
+ void **stack,
+ size_t stack_size,
+ struct hash *loop_set)
+{
+ struct kmod_list *free_list = NULL; /* struct vertex */
+ struct kmod_list *l;
+ struct vertex *root;
+ struct vertex *vertex;
+ struct vertex *v;
+ struct mod *m;
+ struct mod **itr, **itr_end;
+ size_t is;
+ int ret = -ENOMEM;
+
+ root = vertex_new(root_mod, NULL);
+ if (root == NULL) {
+ ERR("No memory to report cycles\n");
+ goto out;
+ }
+
+ l = kmod_list_append(free_list, root);
+ if (l == NULL) {
+ ERR("No memory to report cycles\n");
+ goto out;
+ }
+ free_list = l;
+
+ is = 0;
+ stack[is++] = (void *)root;
+
+ while (is > 0) {
+ vertex = stack[--is];
+ m = vertex->mod;
+ /*
+ * because of the topological sort we can start only
+ * from part of a loop or from a branch after a loop
+ */
+ if (m->visited && m == root->mod) {
+ int rc;
+ rc = depmod_report_one_cycle(depmod, vertex,
+ roots, loop_set);
+ if (rc != 0) {
+ ret = rc;
+ goto out;
+ }
+ continue;
+ }
+
+ m->visited = true;
+ if (m->deps.count == 0) {
+ /*
+ * boundary condition: if there is more than one
+ * single node branch (not a loop), it is
+ * recognized as a loop by the code above:
+ * m->visited because more then one,
+ * m == root->mod since it is a single node.
+ * So, prevent deeping into the branch second
+ * time.
+ */
+ depmod_list_remove_data(roots, m);
+
+ continue;
+ }
+
+ itr = (struct mod **) m->deps.array;
+ itr_end = itr + m->deps.count;
+ for (; itr < itr_end; itr++) {
+ struct mod *dep = *itr;
+ v = vertex_new(dep, vertex);
+ if (v == NULL) {
+ ERR("No memory to report cycles\n");
+ goto out;
+ }
+ assert(is < stack_size);
+ stack[is++] = v;
+
+ l = kmod_list_append(free_list, v);
+ if (l == NULL) {
+ ERR("No memory to report cycles\n");
+ goto out;
+ }
+ free_list = l;
+
+ }
+ }
+ ret = 0;
+
+out:
+ while (free_list) {
+ v = free_list->data;
+ l = kmod_list_remove(free_list);
+ free_list = l;
+ free(v);
+ }
+
+ return ret;
+}
+
+static void depmod_report_cycles(struct depmod *depmod, uint16_t n_mods,
+ uint16_t *users)
+{
+ int num_cyclic = 0;
+ struct kmod_list *roots = NULL; /* struct mod */
+ struct kmod_list *l;
+ size_t n_r; /* local n_roots */
+ int i;
+ int err;
+ _cleanup_free_ void **stack = NULL;
+ struct mod *m;
+ struct mod *root;
+ struct hash *loop_set;
+
+ for (i = 0, n_r = 0; i < n_mods; i++) {
+ if (users[i] <= 0)
+ continue;
+ m = depmod->modules.array[i];
+ l = kmod_list_append(roots, m);
+ if (l == NULL) {
+ ERR("No memory to report cycles\n");
+ goto out_list;
+ }
+ roots = l;
+ n_r++;
+ }
+
+ stack = malloc(n_r * sizeof(void *));
+ if (stack == NULL) {
+ ERR("No memory to report cycles\n");
+ goto out_list;
+ }
+
+ loop_set = hash_new(16, NULL);
+ if (loop_set == NULL) {
+ ERR("No memory to report cycles\n");
+ goto out_list;
+ }
+
+ while (roots != NULL) {
+ root = roots->data;
+ l = kmod_list_remove(roots);
+ roots = l;
+ err = depmod_report_cycles_from_root(depmod,
+ root,
+ &roots,
+ stack, n_r, loop_set);
+ if (err < 0)
+ goto out_hash;
+ }
+
+ num_cyclic = hash_get_count(loop_set);
+ ERR("Found %d modules in dependency cycles!\n", num_cyclic);
+
+out_hash:
+ hash_free(loop_set);
+out_list:
+ while (roots != NULL) {
+ /* no need to free data, come from outside */
+ roots = kmod_list_remove(roots);
+ }
+}
+
+static int depmod_calculate_dependencies(struct depmod *depmod)
+{
+ const struct mod **itrm;
+ uint16_t *users, *roots, *sorted;
+ uint16_t i, n_roots = 0, n_sorted = 0, n_mods = depmod->modules.count;
+ int ret = 0;
+
+ users = malloc(sizeof(uint16_t) * n_mods * 3);
+ if (users == NULL)
+ return -ENOMEM;
+ roots = users + n_mods;
+ sorted = roots + n_mods;
+
+ DBG("calculate dependencies and ordering (%hu modules)\n", n_mods);
+
+ assert(depmod->modules.count < UINT16_MAX);
+
+ /* populate modules users (how many modules uses it) */
+ itrm = (const struct mod **)depmod->modules.array;
+ for (i = 0; i < n_mods; i++, itrm++) {
+ const struct mod *m = *itrm;
+ users[i] = m->users;
+ if (users[i] == 0) {
+ roots[n_roots] = i;
+ n_roots++;
+ }
+ }
+
+ /* topological sort (outputs modules without users first) */
+ while (n_roots > 0) {
+ const struct mod **itr_dst, **itr_dst_end;
+ struct mod *src;
+ uint16_t src_idx = roots[--n_roots];
+
+ src = depmod->modules.array[src_idx];
+ src->dep_sort_idx = n_sorted;
+ sorted[n_sorted] = src_idx;
+ n_sorted++;
+
+ itr_dst = (const struct mod **)src->deps.array;
+ itr_dst_end = itr_dst + src->deps.count;
+ for (; itr_dst < itr_dst_end; itr_dst++) {
+ const struct mod *dst = *itr_dst;
+ uint16_t dst_idx = dst->idx;
+ assert(users[dst_idx] > 0);
+ users[dst_idx]--;
+ if (users[dst_idx] == 0) {
+ roots[n_roots] = dst_idx;
+ n_roots++;
+ }
+ }
+ }
+
+ if (n_sorted < n_mods) {
+ depmod_report_cycles(depmod, n_mods, users);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ depmod_sort_dependencies(depmod);
+
+ DBG("calculated dependencies and ordering (%hu modules)\n", n_mods);
+
+exit:
+ free(users);
+ return ret;
+}
+
+static int depmod_load(struct depmod *depmod)
+{
+ int err;
+
+ err = depmod_load_modules(depmod);
+ if (err < 0)
+ return err;
+
+ err = depmod_load_dependencies(depmod);
+ if (err < 0)
+ return err;
+
+ err = depmod_calculate_dependencies(depmod);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static size_t mod_count_all_dependencies(const struct mod *mod)
+{
+ size_t i, count = 0;
+ for (i = 0; i < mod->deps.count; i++) {
+ const struct mod *d = mod->deps.array[i];
+ count += 1 + mod_count_all_dependencies(d);
+ }
+ return count;
+}
+
+static int mod_fill_all_unique_dependencies(const struct mod *mod, const struct mod **deps, size_t n_deps, size_t *last)
+{
+ size_t i;
+ int err = 0;
+ for (i = 0; i < mod->deps.count; i++) {
+ const struct mod *d = mod->deps.array[i];
+ size_t j;
+ uint8_t exists = 0;
+
+ for (j = 0; j < *last; j++) {
+ if (deps[j] == d) {
+ exists = 1;
+ break;
+ }
+ }
+
+ if (exists)
+ continue;
+
+ if (*last >= n_deps)
+ return -ENOSPC;
+ deps[*last] = d;
+ (*last)++;
+ err = mod_fill_all_unique_dependencies(d, deps, n_deps, last);
+ if (err < 0)
+ break;
+ }
+ return err;
+}
+
+static const struct mod **mod_get_all_sorted_dependencies(const struct mod *mod, size_t *n_deps)
+{
+ const struct mod **deps;
+ size_t last = 0;
+
+ *n_deps = mod_count_all_dependencies(mod);
+ if (*n_deps == 0)
+ return NULL;
+
+ deps = malloc(sizeof(struct mod *) * (*n_deps));
+ if (deps == NULL)
+ return NULL;
+
+ if (mod_fill_all_unique_dependencies(mod, deps, *n_deps, &last) < 0) {
+ free(deps);
+ return NULL;
+ }
+
+ qsort(deps, last, sizeof(struct mod *), dep_cmp);
+ *n_deps = last;
+ return deps;
+}
+
+static inline const char *mod_get_compressed_path(const struct mod *mod)
+{
+ if (mod->relpath != NULL)
+ return mod->relpath;
+ return mod->path;
+}
+
+static int output_deps(struct depmod *depmod, FILE *out)
+{
+ size_t i;
+
+ for (i = 0; i < depmod->modules.count; i++) {
+ const struct mod **deps, *mod = depmod->modules.array[i];
+ const char *p = mod_get_compressed_path(mod);
+ size_t j, n_deps;
+
+ fprintf(out, "%s:", p);
+
+ if (mod->deps.count == 0)
+ goto end;
+
+ deps = mod_get_all_sorted_dependencies(mod, &n_deps);
+ if (deps == NULL) {
+ ERR("could not get all sorted dependencies of %s\n", p);
+ goto end;
+ }
+
+ for (j = 0; j < n_deps; j++) {
+ const struct mod *d = deps[j];
+ fprintf(out, " %s", mod_get_compressed_path(d));
+ }
+ free(deps);
+ end:
+ putc('\n', out);
+ }
+
+ return 0;
+}
+
+static int output_deps_bin(struct depmod *depmod, FILE *out)
+{
+ struct index_node *idx;
+ size_t i;
+
+ if (out == stdout)
+ return 0;
+
+ idx = index_create();
+ if (idx == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < depmod->modules.count; i++) {
+ const struct mod **deps, *mod = depmod->modules.array[i];
+ const char *p = mod_get_compressed_path(mod);
+ char *line;
+ size_t j, n_deps, linepos, linelen, slen;
+ int duplicate;
+
+ deps = mod_get_all_sorted_dependencies(mod, &n_deps);
+ if (deps == NULL && n_deps > 0) {
+ ERR("could not get all sorted dependencies of %s\n", p);
+ continue;
+ }
+
+ linelen = strlen(p) + 1;
+ for (j = 0; j < n_deps; j++) {
+ const struct mod *d = deps[j];
+ linelen += 1 + strlen(mod_get_compressed_path(d));
+ }
+
+ line = malloc(linelen + 1);
+ if (line == NULL) {
+ free(deps);
+ ERR("modules.deps.bin: out of memory\n");
+ continue;
+ }
+
+ linepos = 0;
+ slen = strlen(p);
+ memcpy(line + linepos, p, slen);
+ linepos += slen;
+ line[linepos] = ':';
+ linepos++;
+
+ for (j = 0; j < n_deps; j++) {
+ const struct mod *d = deps[j];
+ const char *dp;
+
+ line[linepos] = ' ';
+ linepos++;
+
+ dp = mod_get_compressed_path(d);
+ slen = strlen(dp);
+ memcpy(line + linepos, dp, slen);
+ linepos += slen;
+ }
+ line[linepos] = '\0';
+
+ duplicate = index_insert(idx, mod->modname, line, mod->idx);
+ if (duplicate && depmod->cfg->warn_dups)
+ WRN("duplicate module deps:\n%s\n", line);
+ free(line);
+ free(deps);
+ }
+
+ index_write(idx, out);
+ index_destroy(idx);
+
+ return 0;
+}
+
+static int output_aliases(struct depmod *depmod, FILE *out)
+{
+ size_t i;
+
+ fputs("# Aliases extracted from modules themselves.\n", out);
+
+ for (i = 0; i < depmod->modules.count; i++) {
+ const struct mod *mod = depmod->modules.array[i];
+ struct kmod_list *l;
+
+ kmod_list_foreach(l, mod->info_list) {
+ const char *key = kmod_module_info_get_key(l);
+ const char *value = kmod_module_info_get_value(l);
+
+ if (!streq(key, "alias"))
+ continue;
+
+ fprintf(out, "alias %s %s\n", value, mod->modname);
+ }
+ }
+
+ return 0;
+}
+
+static int output_aliases_bin(struct depmod *depmod, FILE *out)
+{
+ struct index_node *idx;
+ size_t i;
+
+ if (out == stdout)
+ return 0;
+
+ idx = index_create();
+ if (idx == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < depmod->modules.count; i++) {
+ const struct mod *mod = depmod->modules.array[i];
+ struct kmod_list *l;
+
+ kmod_list_foreach(l, mod->info_list) {
+ const char *key = kmod_module_info_get_key(l);
+ const char *value = kmod_module_info_get_value(l);
+ char buf[PATH_MAX];
+ const char *alias;
+ int duplicate;
+
+ if (!streq(key, "alias"))
+ continue;
+
+ if (alias_normalize(value, buf, NULL) < 0) {
+ WRN("Unmatched bracket in %s\n", value);
+ continue;
+ }
+ alias = buf;
+
+ duplicate = index_insert(idx, alias, mod->modname,
+ mod->idx);
+ if (duplicate && depmod->cfg->warn_dups)
+ WRN("duplicate module alias:\n%s %s\n",
+ alias, mod->modname);
+ }
+ }
+
+ index_write(idx, out);
+ index_destroy(idx);
+
+ return 0;
+}
+
+static int output_softdeps(struct depmod *depmod, FILE *out)
+{
+ size_t i;
+
+ fputs("# Soft dependencies extracted from modules themselves.\n", out);
+
+ for (i = 0; i < depmod->modules.count; i++) {
+ const struct mod *mod = depmod->modules.array[i];
+ struct kmod_list *l;
+
+ kmod_list_foreach(l, mod->info_list) {
+ const char *key = kmod_module_info_get_key(l);
+ const char *value = kmod_module_info_get_value(l);
+
+ if (!streq(key, "softdep"))
+ continue;
+
+ fprintf(out, "softdep %s %s\n", mod->modname, value);
+ }
+ }
+
+ return 0;
+}
+
+static int output_symbols(struct depmod *depmod, FILE *out)
+{
+ struct hash_iter iter;
+ const void *v;
+
+ fputs("# Aliases for symbols, used by symbol_request().\n", out);
+
+ hash_iter_init(depmod->symbols, &iter);
+
+ while (hash_iter_next(&iter, NULL, &v)) {
+ const struct symbol *sym = v;
+ if (sym->owner == NULL)
+ continue;
+
+ fprintf(out, "alias symbol:%s %s\n",
+ sym->name, sym->owner->modname);
+ }
+
+ return 0;
+}
+
+static int output_symbols_bin(struct depmod *depmod, FILE *out)
+{
+ struct index_node *idx;
+ char alias[1024];
+ _cleanup_(scratchbuf_release) struct scratchbuf salias =
+ SCRATCHBUF_INITIALIZER(alias);
+ size_t baselen = sizeof("symbol:") - 1;
+ struct hash_iter iter;
+ const void *v;
+ int ret = 0;
+
+ if (out == stdout)
+ return 0;
+
+ idx = index_create();
+ if (idx == NULL)
+ return -ENOMEM;
+
+ memcpy(alias, "symbol:", baselen);
+
+ hash_iter_init(depmod->symbols, &iter);
+
+ while (hash_iter_next(&iter, NULL, &v)) {
+ int duplicate;
+ const struct symbol *sym = v;
+ size_t len;
+
+ if (sym->owner == NULL)
+ continue;
+
+ len = strlen(sym->name);
+
+ if (scratchbuf_alloc(&salias, baselen + len + 1) < 0) {
+ ret = -ENOMEM;
+ goto err_scratchbuf;
+ }
+ memcpy(scratchbuf_str(&salias) + baselen, sym->name, len + 1);
+ duplicate = index_insert(idx, alias, sym->owner->modname,
+ sym->owner->idx);
+
+ if (duplicate && depmod->cfg->warn_dups)
+ WRN("duplicate module syms:\n%s %s\n",
+ alias, sym->owner->modname);
+ }
+
+ index_write(idx, out);
+
+err_scratchbuf:
+ index_destroy(idx);
+
+ if (ret < 0)
+ ERR("output symbols: %s\n", strerror(-ret));
+
+ return ret;
+}
+
+static int output_builtin_bin(struct depmod *depmod, FILE *out)
+{
+ FILE *in;
+ struct index_node *idx;
+ char line[PATH_MAX], modname[PATH_MAX];
+
+ if (out == stdout)
+ return 0;
+
+ in = dfdopen(depmod->cfg->dirname, "modules.builtin", O_RDONLY, "r");
+ if (in == NULL)
+ return 0;
+
+ idx = index_create();
+ if (idx == NULL) {
+ fclose(in);
+ return -ENOMEM;
+ }
+
+ while (fgets(line, sizeof(line), in) != NULL) {
+ if (!isalpha(line[0])) {
+ ERR("Invalid modules.builtin line: %s\n", line);
+ continue;
+ }
+
+ path_to_modname(line, modname, NULL);
+ index_insert(idx, modname, "", 0);
+ }
+
+ index_write(idx, out);
+ index_destroy(idx);
+ fclose(in);
+
+ return 0;
+}
+
+static int flush_stream(FILE *in, int endchar)
+{
+ size_t i = 0;
+ int c;
+
+ for (c = fgetc(in);
+ c != EOF && c != endchar && c != '\0';
+ c = fgetc(in))
+ ;
+
+ return c == endchar ? i : 0;
+}
+
+static int flush_stream_to(FILE *in, int endchar, char *dst, size_t dst_sz)
+{
+ size_t i = 0;
+ int c;
+
+ for (c = fgetc(in);
+ c != EOF && c != endchar && c != '\0' && i < dst_sz;
+ c = fgetc(in))
+ dst[i++] = c;
+
+ if (i == dst_sz) {
+ WRN("Could not flush stream: %d. Partial content: %.*s\n",
+ ENOSPC, (int) dst_sz, dst);
+ i--;
+ }
+
+ return c == endchar ? i : 0;
+}
+
+static int output_builtin_alias_bin(struct depmod *depmod, FILE *out)
+{
+ FILE *in;
+ struct index_node *idx;
+ int ret;
+
+ if (out == stdout)
+ return 0;
+
+ in = dfdopen(depmod->cfg->dirname, "modules.builtin.modinfo", O_RDONLY, "r");
+ if (in == NULL)
+ return 0;
+
+ idx = index_create();
+ if (idx == NULL) {
+ fclose(in);
+ return -ENOMEM;
+ }
+
+ /* format: modname.key=value\0 */
+ while (!feof(in) && !ferror(in)) {
+ char alias[PATH_MAX];
+ char modname[PATH_MAX];
+ char value[PATH_MAX];
+ size_t len;
+
+ len = flush_stream_to(in, '.', modname, sizeof(modname));
+ modname[len] = '\0';
+ if (!len)
+ continue;
+
+ len = flush_stream_to(in, '=', value, sizeof(value));
+ value[len] = '\0';
+ if (!streq(value, "alias")) {
+ flush_stream(in, '\0');
+ continue;
+ }
+
+ len = flush_stream_to(in, '\0', value, sizeof(value));
+ value[len] = '\0';
+ if (!len)
+ continue;
+
+ alias[0] = '\0';
+ if (alias_normalize(value, alias, NULL) < 0) {
+ WRN("Unmatched bracket in %s\n", value);
+ continue;
+ }
+
+ index_insert(idx, alias, modname, 0);
+ }
+
+ if (ferror(in)) {
+ ret = -EINVAL;
+ } else {
+ index_write(idx, out);
+ ret = 0;
+ }
+
+ index_destroy(idx);
+ fclose(in);
+
+ return ret;
+}
+
+static int output_devname(struct depmod *depmod, FILE *out)
+{
+ size_t i;
+ bool empty = true;
+
+ for (i = 0; i < depmod->modules.count; i++) {
+ const struct mod *mod = depmod->modules.array[i];
+ struct kmod_list *l;
+ const char *devname = NULL;
+ char type = '\0';
+ unsigned int major = 0, minor = 0;
+
+ kmod_list_foreach(l, mod->info_list) {
+ const char *key = kmod_module_info_get_key(l);
+ const char *value = kmod_module_info_get_value(l);
+ unsigned int maj, min;
+
+ if (!streq(key, "alias"))
+ continue;
+
+ if (strstartswith(value, "devname:"))
+ devname = value + sizeof("devname:") - 1;
+ else if (sscanf(value, "char-major-%u-%u",
+ &maj, &min) == 2) {
+ type = 'c';
+ major = maj;
+ minor = min;
+ } else if (sscanf(value, "block-major-%u-%u",
+ &maj, &min) == 2) {
+ type = 'b';
+ major = maj;
+ minor = min;
+ }
+
+ if (type != '\0' && devname != NULL)
+ break;
+ }
+
+ if (devname != NULL) {
+ if (type != '\0') {
+ if (empty) {
+ fputs("# Device nodes to trigger on-demand module loading.\n",
+ out);
+ empty = false;
+ }
+ fprintf(out, "%s %s %c%u:%u\n", mod->modname,
+ devname, type, major, minor);
+ } else
+ ERR("Module '%s' has devname (%s) but "
+ "lacks major and minor information. "
+ "Ignoring.\n", mod->modname, devname);
+ }
+ }
+
+ return 0;
+}
+
+static int depmod_output(struct depmod *depmod, FILE *out)
+{
+ static const struct depfile {
+ const char *name;
+ int (*cb)(struct depmod *depmod, FILE *out);
+ } *itr, depfiles[] = {
+ { "modules.dep", output_deps },
+ { "modules.dep.bin", output_deps_bin },
+ { "modules.alias", output_aliases },
+ { "modules.alias.bin", output_aliases_bin },
+ { "modules.softdep", output_softdeps },
+ { "modules.symbols", output_symbols },
+ { "modules.symbols.bin", output_symbols_bin },
+ { "modules.builtin.bin", output_builtin_bin },
+ { "modules.builtin.alias.bin", output_builtin_alias_bin },
+ { "modules.devname", output_devname },
+ { }
+ };
+ const char *dname = depmod->cfg->outdirname;
+ int dfd, err = 0;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ if (out != NULL)
+ dfd = -1;
+ else {
+ err = mkdir_p(dname, strlen(dname), 0755);
+ if (err < 0) {
+ CRIT("could not create directory %s: %m\n", dname);
+ return err;
+ }
+ dfd = open(dname, O_RDONLY);
+ if (dfd < 0) {
+ err = -errno;
+ CRIT("could not open directory %s: %m\n", dname);
+ return err;
+ }
+ }
+
+ for (itr = depfiles; itr->name != NULL; itr++) {
+ FILE *fp = out;
+ char tmp[NAME_MAX] = "";
+ int r, ferr;
+
+ if (fp == NULL) {
+ int flags = O_CREAT | O_EXCL | O_WRONLY;
+ int mode = 0644;
+ int fd;
+
+ snprintf(tmp, sizeof(tmp), "%s.%i.%li.%li", itr->name, getpid(),
+ tv.tv_usec, tv.tv_sec);
+ fd = openat(dfd, tmp, flags, mode);
+ if (fd < 0) {
+ ERR("openat(%s, %s, %o, %o): %m\n",
+ dname, tmp, flags, mode);
+ continue;
+ }
+ fp = fdopen(fd, "wb");
+ if (fp == NULL) {
+ ERR("fdopen(%d=%s/%s): %m\n", fd, dname, tmp);
+ close(fd);
+ continue;
+ }
+ }
+
+ r = itr->cb(depmod, fp);
+ if (fp == out)
+ continue;
+
+ ferr = ferror(fp) | fclose(fp);
+
+ if (r < 0) {
+ if (unlinkat(dfd, tmp, 0) != 0)
+ ERR("unlinkat(%s, %s): %m\n", dname, tmp);
+
+ ERR("Could not write index '%s': %s\n", itr->name,
+ strerror(-r));
+ err = -errno;
+ break;
+ }
+
+ if (renameat(dfd, tmp, dfd, itr->name) != 0) {
+ err = -errno;
+ CRIT("renameat(%s, %s, %s, %s): %m\n",
+ dname, tmp, dname, itr->name);
+ break;
+ }
+
+ if (ferr) {
+ err = -ENOSPC;
+ ERR("Could not create index '%s'. Output is truncated: %s\n",
+ itr->name, strerror(-err));
+ break;
+ }
+ }
+
+ if (dfd >= 0)
+ close(dfd);
+
+ return err;
+}
+
+static void depmod_add_fake_syms(struct depmod *depmod)
+{
+ /* __this_module is magic inserted by kernel loader. */
+ depmod_symbol_add(depmod, "__this_module", true, 0, NULL);
+ /* On S390, this is faked up too */
+ depmod_symbol_add(depmod, "_GLOBAL_OFFSET_TABLE_", true, 0, NULL);
+ /* On PowerPC64 ABIv2, .TOC. is more or less _GLOBAL_OFFSET_TABLE_ */
+ if (!depmod_symbol_find(depmod, "TOC."))
+ depmod_symbol_add(depmod, "TOC.", true, 0, NULL);
+}
+
+static int depmod_load_symvers(struct depmod *depmod, const char *filename)
+{
+ char line[10240];
+ FILE *fp;
+ unsigned int linenum = 0;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ int err = -errno;
+ DBG("load symvers: %s: %m\n", filename);
+ return err;
+ }
+ DBG("load symvers: %s\n", filename);
+
+ /* eg. "0xb352177e\tfind_first_bit\tvmlinux\tEXPORT_SYMBOL" */
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ const char *ver, *sym, *where;
+ char *verend;
+ uint64_t crc;
+
+ linenum++;
+
+ ver = strtok(line, " \t");
+ sym = strtok(NULL, " \t");
+ where = strtok(NULL, " \t");
+ if (!ver || !sym || !where)
+ continue;
+
+ if (!streq(where, "vmlinux"))
+ continue;
+
+ crc = strtoull(ver, &verend, 16);
+ if (verend[0] != '\0') {
+ ERR("%s:%u Invalid symbol version %s: %m\n",
+ filename, linenum, ver);
+ continue;
+ }
+
+ depmod_symbol_add(depmod, sym, false, crc, NULL);
+ }
+ depmod_add_fake_syms(depmod);
+
+ DBG("loaded symvers: %s\n", filename);
+
+ fclose(fp);
+ return 0;
+}
+
+static int depmod_load_system_map(struct depmod *depmod, const char *filename)
+{
+ const char ksymstr[] = "__ksymtab_";
+ const size_t ksymstr_len = sizeof(ksymstr) - 1;
+ char line[10240];
+ FILE *fp;
+ unsigned int linenum = 0;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ int err = -errno;
+ DBG("load System.map: %s: %m\n", filename);
+ return err;
+ }
+ DBG("load System.map: %s\n", filename);
+
+ /* eg. c0294200 R __ksymtab_devfs_alloc_devnum */
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ char *p, *end;
+
+ linenum++;
+
+ p = strchr(line, ' ');
+ if (p == NULL)
+ goto invalid_syntax;
+ p++;
+ p = strchr(p, ' ');
+ if (p == NULL)
+ goto invalid_syntax;
+ p++;
+
+ /* skip prefix */
+ if (p[0] == depmod->cfg->sym_prefix)
+ p++;
+
+ /* Covers gpl-only and normal symbols. */
+ if (strncmp(p, ksymstr, ksymstr_len) != 0)
+ continue;
+
+ end = strchr(p, '\n');
+ if (end != NULL)
+ *end = '\0';
+
+ depmod_symbol_add(depmod, p + ksymstr_len, true, 0, NULL);
+ continue;
+
+ invalid_syntax:
+ ERR("%s:%u: invalid line: %s\n", filename, linenum, line);
+ }
+ depmod_add_fake_syms(depmod);
+
+ DBG("loaded System.map: %s\n", filename);
+
+ fclose(fp);
+ return 0;
+}
+
+
+static int depfile_up_to_date_dir(DIR *d, time_t mtime, size_t baselen, char *path)
+{
+ struct dirent *de;
+ int err = 1, dfd = dirfd(d);
+
+ while ((de = readdir(d)) != NULL) {
+ const char *name = de->d_name;
+ size_t namelen;
+ struct stat st;
+
+ if (name[0] == '.' && (name[1] == '\0' ||
+ (name[1] == '.' && name[2] == '\0')))
+ continue;
+ if (streq(name, "build") || streq(name, "source"))
+ continue;
+ namelen = strlen(name);
+ if (baselen + namelen + 2 >= PATH_MAX) {
+ path[baselen] = '\0';
+ ERR("path is too long %s%s\n", path, name);
+ continue;
+ }
+
+ if (fstatat(dfd, name, &st, 0) < 0) {
+ ERR("fstatat(%d, %s): %m\n", dfd, name);
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ int fd;
+ DIR *subdir;
+ memcpy(path + baselen, name, namelen + 1);
+ if (baselen + namelen + 2 + NAME_MAX >= PATH_MAX) {
+ ERR("directory path is too long %s\n", path);
+ continue;
+ }
+ fd = openat(dfd, name, O_RDONLY);
+ if (fd < 0) {
+ ERR("openat(%d, %s, O_RDONLY): %m\n",
+ dfd, name);
+ continue;
+ }
+ subdir = fdopendir(fd);
+ if (subdir == NULL) {
+ ERR("fdopendir(%d): %m\n", fd);
+ close(fd);
+ continue;
+ }
+ path[baselen + namelen] = '/';
+ path[baselen + namelen + 1] = '\0';
+ err = depfile_up_to_date_dir(subdir, mtime,
+ baselen + namelen + 1,
+ path);
+ closedir(subdir);
+ } else if (S_ISREG(st.st_mode)) {
+ if (!path_ends_with_kmod_ext(name, namelen))
+ continue;
+
+ memcpy(path + baselen, name, namelen + 1);
+ err = st.st_mtime <= mtime;
+ if (err == 0) {
+ DBG("%s %"PRIu64" is newer than %"PRIu64"\n",
+ path, (uint64_t)st.st_mtime,
+ (uint64_t)mtime);
+ }
+ } else {
+ ERR("unsupported file type %s: %o\n",
+ path, st.st_mode & S_IFMT);
+ continue;
+ }
+
+ if (err == 0)
+ break; /* outdated! */
+ else if (err < 0) {
+ path[baselen + namelen] = '\0';
+ ERR("failed %s: %s\n", path, strerror(-err));
+ err = 1; /* ignore errors */
+ }
+ }
+
+ return err;
+}
+
+/* uptodate: 1, outdated: 0, errors < 0 */
+static int depfile_up_to_date(const char *dirname)
+{
+ char path[PATH_MAX];
+ DIR *d = opendir(dirname);
+ struct stat st;
+ size_t baselen;
+ int err;
+ if (d == NULL) {
+ err = -errno;
+ ERR("could not open directory %s: %m\n", dirname);
+ return err;
+ }
+
+ if (fstatat(dirfd(d), "modules.dep", &st, 0) != 0) {
+ err = -errno;
+ ERR("could not fstatat(%s, modules.dep): %m\n", dirname);
+ closedir(d);
+ return err;
+ }
+
+ baselen = strlen(dirname);
+ memcpy(path, dirname, baselen);
+ path[baselen] = '/';
+ baselen++;
+ path[baselen] = '\0';
+
+ err = depfile_up_to_date_dir(d, st.st_mtime, baselen, path);
+ closedir(d);
+ return err;
+}
+
+static int is_version_number(const char *version)
+{
+ unsigned int d1, d2;
+ return (sscanf(version, "%u.%u", &d1, &d2) == 2);
+}
+
+static int do_depmod(int argc, char *argv[])
+{
+ FILE *out = NULL;
+ int err = 0, all = 0, maybe_all = 0, n_config_paths = 0;
+ _cleanup_free_ char *root = NULL;
+ _cleanup_free_ char *out_root = NULL;
+ _cleanup_free_ const char **config_paths = NULL;
+ const char *system_map = NULL;
+ const char *module_symvers = NULL;
+ const char *null_kmod_config = NULL;
+ struct utsname un;
+ struct kmod_ctx *ctx = NULL;
+ struct cfg cfg;
+ struct depmod depmod;
+
+ memset(&cfg, 0, sizeof(cfg));
+ memset(&depmod, 0, sizeof(depmod));
+
+ for (;;) {
+ int c, idx = 0;
+ c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'a':
+ all = 1;
+ break;
+ case 'A':
+ maybe_all = 1;
+ break;
+ case 'b':
+ if (root)
+ free(root);
+ root = path_make_absolute_cwd(optarg);
+ break;
+ case 'o':
+ if (out_root)
+ free(out_root);
+ out_root = path_make_absolute_cwd(optarg);
+ break;
+ case 'C': {
+ size_t bytes = sizeof(char *) * (n_config_paths + 2);
+ void *tmp = realloc(config_paths, bytes);
+ if (!tmp) {
+ fputs("Error: out-of-memory\n", stderr);
+ goto cmdline_failed;
+ }
+ config_paths = tmp;
+ config_paths[n_config_paths] = optarg;
+ n_config_paths++;
+ config_paths[n_config_paths] = NULL;
+ break;
+ }
+ case 'E':
+ module_symvers = optarg;
+ cfg.check_symvers = 1;
+ break;
+ case 'F':
+ system_map = optarg;
+ break;
+ case 'e':
+ cfg.print_unknown = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'n':
+ out = stdout;
+ break;
+ case 'P':
+ if (optarg[1] != '\0') {
+ CRIT("-P only takes a single char\n");
+ goto cmdline_failed;
+ }
+ cfg.sym_prefix = optarg[0];
+ break;
+ case 'w':
+ cfg.warn_dups = 1;
+ break;
+ case 'u':
+ case 'q':
+ case 'r':
+ case 'm':
+ if (idx > 0)
+ WRN("Ignored deprecated option --%s\n",
+ cmdopts[idx].name);
+ else
+ WRN("Ignored deprecated option -%c\n", c);
+
+ break;
+ case 'h':
+ help();
+ return EXIT_SUCCESS;
+ case 'V':
+ puts(PACKAGE " version " VERSION);
+ puts(KMOD_FEATURES);
+ return EXIT_SUCCESS;
+ case '?':
+ goto cmdline_failed;
+ default:
+ ERR("unexpected getopt_long() value '%c'.\n", c);
+ goto cmdline_failed;
+ }
+ }
+
+ if (optind < argc) {
+ if (!is_version_number(argv[optind])) {
+ ERR("Bad version passed %s\n", argv[optind]);
+ goto cmdline_failed;
+ }
+ cfg.kversion = argv[optind];
+ optind++;
+ } else {
+ if (uname(&un) < 0) {
+ CRIT("uname() failed: %s\n", strerror(errno));
+ goto cmdline_failed;
+ }
+ cfg.kversion = un.release;
+ }
+
+ cfg.dirnamelen = snprintf(cfg.dirname, PATH_MAX,
+ "%s" MODULE_DIRECTORY "/%s",
+ root ?: "", cfg.kversion);
+
+ cfg.outdirnamelen = snprintf(cfg.outdirname, PATH_MAX,
+ "%s" MODULE_DIRECTORY "/%s",
+ out_root ?: (root ?: ""), cfg.kversion);
+
+ if (optind == argc)
+ all = 1;
+
+ if (maybe_all) {
+ if (out == stdout)
+ goto done;
+ /* ignore up-to-date errors (< 0) */
+ if (depfile_up_to_date(cfg.dirname) == 1)
+ goto done;
+ all = 1;
+ }
+
+ ctx = kmod_new(cfg.dirname, &null_kmod_config);
+ if (ctx == NULL) {
+ CRIT("kmod_new(\"%s\", {NULL}) failed: %m\n", cfg.dirname);
+ goto cmdline_failed;
+ }
+
+ log_setup_kmod_log(ctx, verbose);
+
+ err = depmod_init(&depmod, &cfg, ctx);
+ if (err < 0) {
+ CRIT("depmod_init: %s\n", strerror(-err));
+ goto depmod_init_failed;
+ }
+ ctx = NULL; /* owned by depmod */
+
+ if (module_symvers != NULL) {
+ err = depmod_load_symvers(&depmod, module_symvers);
+ if (err < 0) {
+ CRIT("could not load %s: %s\n", module_symvers,
+ strerror(-err));
+ goto cmdline_failed;
+ }
+ } else if (system_map != NULL) {
+ err = depmod_load_system_map(&depmod, system_map);
+ if (err < 0) {
+ CRIT("could not load %s: %s\n", system_map,
+ strerror(-err));
+ goto cmdline_failed;
+ }
+ } else if (cfg.print_unknown) {
+ WRN("-e needs -E or -F\n");
+ cfg.print_unknown = 0;
+ }
+
+ if (all) {
+ err = cfg_load(&cfg, config_paths);
+ if (err < 0) {
+ CRIT("could not load configuration files\n");
+ goto cmdline_modules_failed;
+ }
+ err = depmod_modules_search(&depmod);
+ if (err < 0) {
+ CRIT("could not search modules: %s\n", strerror(-err));
+ goto cmdline_modules_failed;
+ }
+ } else {
+ int i;
+
+ for (i = optind; i < argc; i++) {
+ const char *path = argv[i];
+ struct kmod_module *mod;
+
+ if (path[0] != '/') {
+ CRIT("%s: not absolute path.\n", path);
+ goto cmdline_modules_failed;
+ }
+
+ err = kmod_module_new_from_path(depmod.ctx, path, &mod);
+ if (err < 0) {
+ CRIT("could not create module %s: %s\n",
+ path, strerror(-err));
+ goto cmdline_modules_failed;
+ }
+
+ err = depmod_module_add(&depmod, mod);
+ if (err < 0) {
+ CRIT("could not add module %s: %s\n",
+ path, strerror(-err));
+ kmod_module_unref(mod);
+ goto cmdline_modules_failed;
+ }
+ }
+ }
+
+ err = depmod_modules_build_array(&depmod);
+ if (err < 0) {
+ CRIT("could not build module array: %s\n",
+ strerror(-err));
+ goto cmdline_modules_failed;
+ }
+
+ depmod_modules_sort(&depmod);
+ err = depmod_load(&depmod);
+ if (err < 0)
+ goto cmdline_modules_failed;
+
+ err = depmod_output(&depmod, out);
+
+done:
+ depmod_shutdown(&depmod);
+ cfg_free(&cfg);
+ return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+
+cmdline_modules_failed:
+ depmod_shutdown(&depmod);
+depmod_init_failed:
+ if (ctx != NULL)
+ kmod_unref(ctx);
+cmdline_failed:
+ cfg_free(&cfg);
+ return EXIT_FAILURE;
+}
+
+const struct kmod_cmd kmod_cmd_compat_depmod = {
+ .name = "depmod",
+ .cmd = do_depmod,
+ .help = "compat depmod command",
+};
diff --git a/tools/insert.c b/tools/insert.c
new file mode 100644
index 0000000..0ebcef9
--- /dev/null
+++ b/tools/insert.c
@@ -0,0 +1,128 @@
+/*
+ * kmod-insert - insert a module into the kernel.
+ *
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libkmod/libkmod.h>
+
+#include "kmod.h"
+
+static const char cmdopts_s[] = "h";
+static const struct option cmdopts[] = {
+ {"help", no_argument, 0, 'h'},
+ { }
+};
+
+static void help(void)
+{
+ printf("Usage:\n"
+ "\t%s insert [options] module\n"
+ "Options:\n"
+ "\t-h, --help show this help\n",
+ program_invocation_short_name);
+}
+
+static const char *mod_strerror(int err)
+{
+ switch (err) {
+ case KMOD_PROBE_APPLY_BLACKLIST:
+ return "Module is blacklisted";
+ case -EEXIST:
+ return "Module already in kernel";
+ case -ENOENT:
+ return "Unknown symbol in module or unknown parameter (see dmesg)";
+ default:
+ return strerror(-err);
+ }
+}
+
+static int do_insert(int argc, char *argv[])
+{
+ struct kmod_ctx *ctx;
+ struct kmod_list *list = NULL, *l;
+ const char *name;
+ int err, r = EXIT_SUCCESS;
+
+ for (;;) {
+ int c, idx = 0;
+ c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'h':
+ help();
+ return EXIT_SUCCESS;
+ default:
+ ERR("Unexpected getopt_long() value '%c'.\n", c);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind >= argc) {
+ ERR("Missing module name\n");
+ return EXIT_FAILURE;
+ }
+
+ ctx = kmod_new(NULL, NULL);
+ if (!ctx) {
+ ERR("kmod_new() failed!\n");
+ return EXIT_FAILURE;
+ }
+
+ name = argv[optind];
+ err = kmod_module_new_from_lookup(ctx, name, &list);
+ if (err < 0) {
+ ERR("Could not lookup module matching '%s': %s\n", name, strerror(-err));
+ r = EXIT_FAILURE;
+ goto end;
+ }
+
+ if (list == NULL) {
+ ERR("No module matches '%s'\n", name);
+ r = EXIT_FAILURE;
+ goto end;
+ }
+
+ 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 != 0) {
+ r = EXIT_FAILURE;
+ ERR("Could not insert '%s': %s\n", kmod_module_get_name(mod), mod_strerror(err));
+ }
+
+ kmod_module_unref(mod);
+ }
+
+ kmod_module_unref_list(list);
+end:
+ kmod_unref(ctx);
+ return r;
+}
+
+const struct kmod_cmd kmod_cmd_insert = {
+ .name = "insert",
+ .cmd = do_insert,
+ .help = "insert a module into the kernel",
+};
diff --git a/tools/insmod.c b/tools/insmod.c
new file mode 100644
index 0000000..c422971
--- /dev/null
+++ b/tools/insmod.c
@@ -0,0 +1,166 @@
+/*
+ * kmod-insmod - insert modules into linux kernel using libkmod.
+ *
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/util.h>
+
+#include <libkmod/libkmod.h>
+
+#include "kmod.h"
+
+static const char cmdopts_s[] = "psfVh";
+static const struct option cmdopts[] = {
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {NULL, 0, 0, 0}
+};
+
+static void help(void)
+{
+ printf("Usage:\n"
+ "\t%s [options] filename [args]\n"
+ "Options:\n"
+ "\t-V, --version show version\n"
+ "\t-h, --help show this help\n",
+ program_invocation_short_name);
+}
+
+static const char *mod_strerror(int err)
+{
+ switch (err) {
+ case ENOEXEC:
+ return "Invalid module format";
+ case ENOENT:
+ return "Unknown symbol in module";
+ case ESRCH:
+ return "Module has wrong symbol version";
+ case EINVAL:
+ return "Invalid parameters";
+ default:
+ return strerror(err);
+ }
+}
+
+static int do_insmod(int argc, char *argv[])
+{
+ struct kmod_ctx *ctx;
+ struct kmod_module *mod;
+ const char *filename;
+ char *opts = NULL;
+ size_t optslen = 0;
+ int i, err;
+ const char *null_config = NULL;
+ unsigned int flags = 0;
+
+ for (;;) {
+ int c, idx = 0;
+ c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'p':
+ case 's':
+ /* ignored, for compatibility only */
+ break;
+ case 'f':
+ flags |= KMOD_PROBE_FORCE_MODVERSION;
+ flags |= KMOD_PROBE_FORCE_VERMAGIC;
+ break;
+ case 'h':
+ help();
+ return EXIT_SUCCESS;
+ case 'V':
+ puts(PACKAGE " version " VERSION);
+ puts(KMOD_FEATURES);
+ return EXIT_SUCCESS;
+ case '?':
+ return EXIT_FAILURE;
+ default:
+ ERR("unexpected getopt_long() value '%c'.\n",
+ c);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind >= argc) {
+ ERR("missing filename.\n");
+ return EXIT_FAILURE;
+ }
+
+ filename = argv[optind];
+ if (streq(filename, "-")) {
+ ERR("this tool does not support loading from stdin!\n");
+ return EXIT_FAILURE;
+ }
+
+ for (i = optind + 1; i < argc; i++) {
+ size_t len = strlen(argv[i]);
+ void *tmp = realloc(opts, optslen + len + 2);
+ if (tmp == NULL) {
+ ERR("out of memory\n");
+ free(opts);
+ return EXIT_FAILURE;
+ }
+ opts = tmp;
+ if (optslen > 0) {
+ opts[optslen] = ' ';
+ optslen++;
+ }
+ memcpy(opts + optslen, argv[i], len);
+ optslen += len;
+ opts[optslen] = '\0';
+ }
+
+ ctx = kmod_new(NULL, &null_config);
+ if (!ctx) {
+ ERR("kmod_new() failed!\n");
+ free(opts);
+ return EXIT_FAILURE;
+ }
+
+ err = kmod_module_new_from_path(ctx, filename, &mod);
+ if (err < 0) {
+ ERR("could not load module %s: %s\n", filename,
+ strerror(-err));
+ goto end;
+ }
+
+ err = kmod_module_insert_module(mod, flags, opts);
+ if (err < 0) {
+ ERR("could not insert module %s: %s\n", filename,
+ mod_strerror(-err));
+ }
+ kmod_module_unref(mod);
+
+end:
+ kmod_unref(ctx);
+ free(opts);
+ return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+const struct kmod_cmd kmod_cmd_compat_insmod = {
+ .name = "insmod",
+ .cmd = do_insmod,
+ .help = "compat insmod command",
+};
diff --git a/tools/kmod.c b/tools/kmod.c
new file mode 100644
index 0000000..55689c0
--- /dev/null
+++ b/tools/kmod.c
@@ -0,0 +1,179 @@
+/*
+ * kmod - one tool to rule them all
+ *
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/util.h>
+
+#include <libkmod/libkmod.h>
+
+#include "kmod.h"
+
+static const char options_s[] = "+hV";
+static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ {}
+};
+
+static const struct kmod_cmd kmod_cmd_help;
+
+static const struct kmod_cmd *kmod_cmds[] = {
+ &kmod_cmd_help,
+ &kmod_cmd_list,
+ &kmod_cmd_static_nodes,
+
+#ifdef ENABLE_EXPERIMENTAL
+ &kmod_cmd_insert,
+ &kmod_cmd_remove,
+#endif
+};
+
+static const struct kmod_cmd *kmod_compat_cmds[] = {
+ &kmod_cmd_compat_lsmod,
+ &kmod_cmd_compat_rmmod,
+ &kmod_cmd_compat_insmod,
+ &kmod_cmd_compat_modinfo,
+ &kmod_cmd_compat_modprobe,
+ &kmod_cmd_compat_depmod,
+};
+
+static int kmod_help(int argc, char *argv[])
+{
+ size_t i;
+
+ printf("kmod - Manage kernel modules: list, load, unload, etc\n"
+ "Usage:\n"
+ "\t%s [options] command [command_options]\n\n"
+ "Options:\n"
+ "\t-V, --version show version\n"
+ "\t-h, --help show this help\n\n"
+ "Commands:\n", basename(argv[0]));
+
+ for (i = 0; i < ARRAY_SIZE(kmod_cmds); i++) {
+ if (kmod_cmds[i]->help != NULL) {
+ printf(" %-12s %s\n", kmod_cmds[i]->name,
+ kmod_cmds[i]->help);
+ }
+ }
+
+ puts("\nkmod also handles gracefully if called from following symlinks:");
+
+ for (i = 0; i < ARRAY_SIZE(kmod_compat_cmds); i++) {
+ if (kmod_compat_cmds[i]->help != NULL) {
+ printf(" %-12s %s\n", kmod_compat_cmds[i]->name,
+ kmod_compat_cmds[i]->help);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static const struct kmod_cmd kmod_cmd_help = {
+ .name = "help",
+ .cmd = kmod_help,
+ .help = "Show help message",
+};
+
+static int handle_kmod_commands(int argc, char *argv[])
+{
+ const char *cmd;
+ int err = 0;
+ size_t i;
+
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, options_s, options, NULL);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ kmod_help(argc, argv);
+ return EXIT_SUCCESS;
+ case 'V':
+ puts(PACKAGE " version " VERSION);
+ puts(KMOD_FEATURES);
+ return EXIT_SUCCESS;
+ case '?':
+ return EXIT_FAILURE;
+ default:
+ fprintf(stderr, "Error: unexpected getopt_long() value '%c'.\n", c);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind >= argc) {
+ fputs("missing command\n", stderr);
+ goto fail;
+ }
+
+ cmd = argv[optind];
+
+ for (i = 0, err = -EINVAL; i < ARRAY_SIZE(kmod_cmds); i++) {
+ if (streq(kmod_cmds[i]->name, cmd)) {
+ err = kmod_cmds[i]->cmd(--argc, ++argv);
+ break;
+ }
+ }
+
+ if (err < 0) {
+ fprintf(stderr, "invalid command '%s'\n", cmd);
+ goto fail;
+ }
+
+ return err;
+
+fail:
+ kmod_help(argc, argv);
+ return EXIT_FAILURE;
+}
+
+
+static int handle_kmod_compat_commands(int argc, char *argv[])
+{
+ const char *cmd;
+ size_t i;
+
+ cmd = basename(argv[0]);
+
+ for (i = 0; i < ARRAY_SIZE(kmod_compat_cmds); i++) {
+ if (streq(kmod_compat_cmds[i]->name, cmd))
+ return kmod_compat_cmds[i]->cmd(argc, argv);
+ }
+
+ return -ENOENT;
+}
+
+int main(int argc, char *argv[])
+{
+ int err;
+
+ if (streq(program_invocation_short_name, "kmod"))
+ err = handle_kmod_commands(argc, argv);
+ else
+ err = handle_kmod_compat_commands(argc, argv);
+
+ return err;
+}
diff --git a/tools/kmod.h b/tools/kmod.h
new file mode 100644
index 0000000..1770786
--- /dev/null
+++ b/tools/kmod.h
@@ -0,0 +1,42 @@
+/*
+ * kmod - one tool to rule them all
+ *
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <shared/macro.h>
+
+struct kmod_cmd {
+ const char *name;
+ int (*cmd)(int argc, char *argv[]);
+ const char *help;
+};
+
+extern const struct kmod_cmd kmod_cmd_compat_lsmod;
+extern const struct kmod_cmd kmod_cmd_compat_rmmod;
+extern const struct kmod_cmd kmod_cmd_compat_insmod;
+extern const struct kmod_cmd kmod_cmd_compat_modinfo;
+extern const struct kmod_cmd kmod_cmd_compat_modprobe;
+extern const struct kmod_cmd kmod_cmd_compat_depmod;
+
+extern const struct kmod_cmd kmod_cmd_insert;
+extern const struct kmod_cmd kmod_cmd_list;
+extern const struct kmod_cmd kmod_cmd_static_nodes;
+extern const struct kmod_cmd kmod_cmd_remove;
+
+#include "log.h"
diff --git a/tools/kmod.pc.in b/tools/kmod.pc.in
new file mode 100644
index 0000000..97215c8
--- /dev/null
+++ b/tools/kmod.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+sysconfdir=@sysconfdir@
+distconfdir=@distconfdir@
+module_directory=@module_directory@
+module_compressions=@module_compressions@
+module_signatures=@module_signatures@
+
+Name: kmod
+Description: Tools to deal with kernel modules
+Version: @VERSION@
diff --git a/tools/log.c b/tools/log.c
new file mode 100644
index 0000000..3317a35
--- /dev/null
+++ b/tools/log.c
@@ -0,0 +1,149 @@
+/*
+ * kmod - log infrastructure
+ *
+ * Copyright (C) 2012-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <libkmod/libkmod.h>
+
+#include "kmod.h"
+
+#define PRIO_MAX_SIZE 32
+
+static bool log_use_syslog;
+static int log_priority = LOG_WARNING;
+
+static const char *prio_to_str(char buf[static PRIO_MAX_SIZE], int prio)
+{
+ const char *prioname;
+
+ switch (prio) {
+ case LOG_CRIT:
+ prioname = "FATAL";
+ break;
+ case LOG_ERR:
+ prioname = "ERROR";
+ break;
+ case LOG_WARNING:
+ prioname = "WARNING";
+ break;
+ case LOG_NOTICE:
+ prioname = "NOTICE";
+ break;
+ case LOG_INFO:
+ prioname = "INFO";
+ break;
+ case LOG_DEBUG:
+ prioname = "DEBUG";
+ break;
+ default:
+ snprintf(buf, PRIO_MAX_SIZE, "LOG-%03d", prio);
+ prioname = buf;
+ }
+
+ return prioname;
+}
+
+_printf_format_(6, 0)
+static void log_kmod(void *data, int priority, const char *file, int line,
+ const char *fn, const char *format, va_list args)
+{
+ char buf[PRIO_MAX_SIZE];
+ const char *prioname;
+ char *str;
+
+ prioname = prio_to_str(buf, priority);
+
+ if (vasprintf(&str, format, args) < 0)
+ return;
+
+ if (log_use_syslog) {
+#ifdef ENABLE_DEBUG
+ syslog(priority, "%s: %s:%d %s() %s", prioname, file, line,
+ fn, str);
+#else
+ syslog(priority, "%s: %s", prioname, str);
+#endif
+ } else {
+#ifdef ENABLE_DEBUG
+ fprintf(stderr, "%s: %s: %s:%d %s() %s",
+ program_invocation_short_name, prioname, file, line,
+ fn, str);
+#else
+ fprintf(stderr, "%s: %s: %s", program_invocation_short_name,
+ prioname, str);
+#endif
+ }
+
+ free(str);
+ (void)data;
+}
+void log_open(bool use_syslog)
+{
+ log_use_syslog = use_syslog;
+
+ if (log_use_syslog)
+ openlog(program_invocation_short_name, LOG_CONS, LOG_DAEMON);
+}
+
+void log_close(void)
+{
+ if (log_use_syslog)
+ closelog();
+}
+
+void log_printf(int prio, const char *fmt, ...)
+{
+ char buf[PRIO_MAX_SIZE];
+ const char *prioname;
+ char *msg;
+ va_list args;
+
+ if (prio > log_priority)
+ return;
+
+ va_start(args, fmt);
+ if (vasprintf(&msg, fmt, args) < 0)
+ msg = NULL;
+ va_end(args);
+ if (msg == NULL)
+ return;
+
+ prioname = prio_to_str(buf, prio);
+
+ if (log_use_syslog)
+ syslog(prio, "%s: %s", prioname, msg);
+ else
+ fprintf(stderr, "%s: %s: %s", program_invocation_short_name,
+ prioname, msg);
+ free(msg);
+
+ if (prio <= LOG_CRIT)
+ exit(EXIT_FAILURE);
+}
+
+void log_setup_kmod_log(struct kmod_ctx *ctx, int priority)
+{
+ log_priority = priority;
+
+ kmod_set_log_priority(ctx, log_priority);
+ kmod_set_log_fn(ctx, log_kmod, NULL);
+}
diff --git a/tools/log.h b/tools/log.h
new file mode 100644
index 0000000..d55a4c6
--- /dev/null
+++ b/tools/log.h
@@ -0,0 +1,37 @@
+/*
+ * kmod - log infrastructure
+ *
+ * Copyright (C) 2012-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "kmod.h"
+
+void log_open(bool use_syslog);
+void log_close(void);
+void log_printf(int prio, const char *fmt, ...) _printf_format_(2, 3);
+#define CRIT(...) log_printf(LOG_CRIT, __VA_ARGS__)
+#define ERR(...) log_printf(LOG_ERR, __VA_ARGS__)
+#define WRN(...) log_printf(LOG_WARNING, __VA_ARGS__)
+#define INF(...) log_printf(LOG_INFO, __VA_ARGS__)
+#define DBG(...) log_printf(LOG_DEBUG, __VA_ARGS__)
+
+struct kmod_ctx;
+void log_setup_kmod_log(struct kmod_ctx *ctx, int priority);
diff --git a/tools/lsmod.c b/tools/lsmod.c
new file mode 100644
index 0000000..d9a27f2
--- /dev/null
+++ b/tools/lsmod.c
@@ -0,0 +1,102 @@
+/*
+ * kmod-lsmod - list modules from linux kernel using libkmod.
+ *
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libkmod/libkmod.h>
+
+#include "kmod.h"
+
+static int do_lsmod(int argc, char *argv[])
+{
+ struct kmod_ctx *ctx;
+ const char *null_config = NULL;
+ struct kmod_list *list, *itr;
+ int err;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: %s\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ ctx = kmod_new(NULL, &null_config);
+ if (ctx == NULL) {
+ fputs("Error: kmod_new() failed!\n", stderr);
+ return EXIT_FAILURE;
+ }
+
+ err = kmod_module_new_from_loaded(ctx, &list);
+ if (err < 0) {
+ fprintf(stderr, "Error: could not get list of modules: %s\n",
+ strerror(-err));
+ kmod_unref(ctx);
+ return EXIT_FAILURE;
+ }
+
+ puts("Module Size Used by");
+
+ kmod_list_foreach(itr, list) {
+ struct kmod_module *mod = kmod_module_get_module(itr);
+ const char *name = kmod_module_get_name(mod);
+ int use_count = kmod_module_get_refcnt(mod);
+ long size = kmod_module_get_size(mod);
+ struct kmod_list *holders, *hitr;
+ int first = 1;
+
+ printf("%-19s %8ld %d", name, size, use_count);
+ holders = kmod_module_get_holders(mod);
+ kmod_list_foreach(hitr, holders) {
+ struct kmod_module *hm = kmod_module_get_module(hitr);
+
+ if (!first) {
+ putchar(',');
+ } else {
+ putchar(' ');
+ first = 0;
+ }
+
+ fputs(kmod_module_get_name(hm), stdout);
+ kmod_module_unref(hm);
+ }
+ putchar('\n');
+ kmod_module_unref_list(holders);
+ kmod_module_unref(mod);
+ }
+ kmod_module_unref_list(list);
+ kmod_unref(ctx);
+
+ return EXIT_SUCCESS;
+}
+
+const struct kmod_cmd kmod_cmd_compat_lsmod = {
+ .name = "lsmod",
+ .cmd = do_lsmod,
+ .help = "compat lsmod command",
+};
+
+const struct kmod_cmd kmod_cmd_list = {
+ .name = "list",
+ .cmd = do_lsmod,
+ .help = "list currently loaded modules",
+};
diff --git a/tools/modinfo.c b/tools/modinfo.c
new file mode 100644
index 0000000..cacc32d
--- /dev/null
+++ b/tools/modinfo.c
@@ -0,0 +1,500 @@
+/*
+ * kmod-modinfo - query kernel module information using libkmod.
+ *
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include <shared/util.h>
+
+#include <libkmod/libkmod.h>
+
+#include "kmod.h"
+
+static char separator = '\n';
+static const char *field = NULL;
+
+struct param {
+ struct param *next;
+ const char *name;
+ const char *param;
+ const char *type;
+ int namelen;
+ int paramlen;
+ int typelen;
+};
+
+static struct param *add_param(const char *name, int namelen, const char *param, int paramlen, const char *type, int typelen, struct param **list)
+{
+ struct param *it;
+
+ for (it = *list; it != NULL; it = it->next) {
+ if (it->namelen == namelen &&
+ memcmp(it->name, name, namelen) == 0)
+ break;
+ }
+
+ if (it == NULL) {
+ it = malloc(sizeof(struct param));
+ if (it == NULL)
+ return NULL;
+ it->next = *list;
+ *list = it;
+ it->name = name;
+ it->namelen = namelen;
+ it->param = NULL;
+ it->type = NULL;
+ it->paramlen = 0;
+ it->typelen = 0;
+ }
+
+ if (param != NULL) {
+ it->param = param;
+ it->paramlen = paramlen;
+ }
+
+ if (type != NULL) {
+ it->type = type;
+ it->typelen = typelen;
+ }
+
+ return it;
+}
+
+static int process_parm(const char *key, const char *value, struct param **params)
+{
+ const char *name, *param, *type;
+ int namelen, paramlen, typelen;
+ struct param *it;
+ const char *colon = strchr(value, ':');
+ if (colon == NULL) {
+ ERR("Found invalid \"%s=%s\": missing ':'\n",
+ key, value);
+ return 0;
+ }
+
+ name = value;
+ namelen = colon - value;
+ if (streq(key, "parm")) {
+ param = colon + 1;
+ paramlen = strlen(param);
+ type = NULL;
+ typelen = 0;
+ } else {
+ param = NULL;
+ paramlen = 0;
+ type = colon + 1;
+ typelen = strlen(type);
+ }
+
+ it = add_param(name, namelen, param, paramlen, type, typelen, params);
+ if (it == NULL) {
+ ERR("Out of memory!\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int modinfo_params_do(const struct kmod_list *list)
+{
+ const struct kmod_list *l;
+ struct param *params = NULL;
+ int err = 0;
+
+ kmod_list_foreach(l, list) {
+ const char *key = kmod_module_info_get_key(l);
+ const char *value = kmod_module_info_get_value(l);
+ if (!streq(key, "parm") && !streq(key, "parmtype"))
+ continue;
+
+ err = process_parm(key, value, &params);
+ if (err < 0)
+ goto end;
+ }
+
+ while (params != NULL) {
+ struct param *p = params;
+ params = p->next;
+
+ if (p->param == NULL)
+ printf("%.*s: (%.*s)%c",
+ p->namelen, p->name, p->typelen, p->type,
+ separator);
+ else if (p->type != NULL)
+ printf("%.*s:%.*s (%.*s)%c",
+ p->namelen, p->name,
+ p->paramlen, p->param,
+ p->typelen, p->type,
+ separator);
+ else
+ printf("%.*s:%.*s%c",
+ p->namelen, p->name,
+ p->paramlen, p->param,
+ separator);
+
+ free(p);
+ }
+
+end:
+ while (params != NULL) {
+ void *tmp = params;
+ params = params->next;
+ free(tmp);
+ }
+
+ return err;
+}
+
+static int modinfo_do(struct kmod_module *mod)
+{
+ struct kmod_list *l, *list = NULL;
+ struct param *params = NULL;
+ int err, is_builtin;
+ const char *filename = kmod_module_get_path(mod);
+
+ is_builtin = (filename == NULL);
+
+ if (is_builtin) {
+ if (field == NULL)
+ printf("%-16s%s%c", "name:",
+ kmod_module_get_name(mod), separator);
+ else if (field != NULL && streq(field, "name"))
+ printf("%s%c", kmod_module_get_name(mod), separator);
+ filename = "(builtin)";
+ }
+
+ if (field != NULL && streq(field, "filename")) {
+ printf("%s%c", filename, separator);
+ return 0;
+ } else if (field == NULL) {
+ printf("%-16s%s%c", "filename:",
+ filename, separator);
+ }
+
+ err = kmod_module_get_info(mod, &list);
+ if (err < 0) {
+ if (is_builtin && err == -ENOENT) {
+ /*
+ * This is an old kernel that does not have a file
+ * with information about built-in modules.
+ */
+ return 0;
+ }
+ ERR("could not get modinfo from '%s': %s\n",
+ kmod_module_get_name(mod), strerror(-err));
+ return err;
+ }
+
+ if (field != NULL && streq(field, "parm")) {
+ err = modinfo_params_do(list);
+ goto end;
+ }
+
+ kmod_list_foreach(l, list) {
+ const char *key = kmod_module_info_get_key(l);
+ const char *value = kmod_module_info_get_value(l);
+ int keylen;
+
+ if (field != NULL) {
+ if (!streq(field, key))
+ continue;
+ /* filtered output contains no key, just value */
+ printf("%s%c", value, separator);
+ continue;
+ }
+
+ if (streq(key, "parm") || streq(key, "parmtype")) {
+ err = process_parm(key, value, &params);
+ if (err < 0)
+ goto end;
+ continue;
+ }
+
+ if (separator == '\0') {
+ printf("%s=%s%c", key, value, separator);
+ continue;
+ }
+
+ keylen = strlen(key);
+ printf("%s:%-*s%s%c", key, 15 - keylen, "", value, separator);
+ }
+
+ if (field != NULL)
+ goto end;
+
+ while (params != NULL) {
+ struct param *p = params;
+ params = p->next;
+
+ if (p->param == NULL)
+ printf("%-16s%.*s:%.*s%c", "parm:",
+ p->namelen, p->name, p->typelen, p->type,
+ separator);
+ else if (p->type != NULL)
+ printf("%-16s%.*s:%.*s (%.*s)%c", "parm:",
+ p->namelen, p->name,
+ p->paramlen, p->param,
+ p->typelen, p->type,
+ separator);
+ else
+ printf("%-16s%.*s:%.*s%c",
+ "parm:",
+ p->namelen, p->name,
+ p->paramlen, p->param,
+ separator);
+
+ free(p);
+ }
+
+end:
+ while (params != NULL) {
+ void *tmp = params;
+ params = params->next;
+ free(tmp);
+ }
+ kmod_module_info_free_list(list);
+
+ return err;
+}
+
+static int modinfo_path_do(struct kmod_ctx *ctx, const char *path)
+{
+ struct kmod_module *mod;
+ int err = kmod_module_new_from_path(ctx, path, &mod);
+ if (err < 0) {
+ ERR("Module file %s not found.\n", path);
+ return err;
+ }
+ err = modinfo_do(mod);
+ kmod_module_unref(mod);
+ return err;
+}
+
+static int modinfo_name_do(struct kmod_ctx *ctx, const char *name)
+{
+ struct kmod_module *mod = NULL;
+ int err;
+
+ err = kmod_module_new_from_name_lookup(ctx, name, &mod);
+ if (err < 0 || mod == NULL) {
+ ERR("Module name %s not found.\n", name);
+ return err < 0 ? err : -ENOENT;
+ }
+
+ err = modinfo_do(mod);
+ kmod_module_unref(mod);
+
+ return err;
+}
+
+
+static int modinfo_alias_do(struct kmod_ctx *ctx, const char *alias)
+{
+ struct kmod_list *l, *list = NULL;
+ int err = kmod_module_new_from_lookup(ctx, alias, &list);
+ if (err < 0) {
+ ERR("Module alias %s not found.\n", alias);
+ return err;
+ }
+
+ if (list == NULL) {
+ ERR("Module %s not found.\n", alias);
+ return -ENOENT;
+ }
+
+ kmod_list_foreach(l, list) {
+ struct kmod_module *mod = kmod_module_get_module(l);
+ int r = modinfo_do(mod);
+ kmod_module_unref(mod);
+ if (r < 0)
+ err = r;
+ }
+ kmod_module_unref_list(list);
+ return err;
+}
+
+static const char cmdopts_s[] = "adlpn0mF:k:b:Vh";
+static const struct option cmdopts[] = {
+ {"author", no_argument, 0, 'a'},
+ {"description", no_argument, 0, 'd'},
+ {"license", no_argument, 0, 'l'},
+ {"parameters", no_argument, 0, 'p'},
+ {"filename", no_argument, 0, 'n'},
+ {"null", no_argument, 0, '0'},
+ {"modname", no_argument, 0, 'm'},
+ {"field", required_argument, 0, 'F'},
+ {"set-version", required_argument, 0, 'k'},
+ {"basedir", required_argument, 0, 'b'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {NULL, 0, 0, 0}
+};
+
+static void help(void)
+{
+ printf("Usage:\n"
+ "\t%s [options] <modulename|filename> [args]\n"
+ "Options:\n"
+ "\t-a, --author Print only 'author'\n"
+ "\t-d, --description Print only 'description'\n"
+ "\t-l, --license Print only 'license'\n"
+ "\t-p, --parameters Print only 'parm'\n"
+ "\t-n, --filename Print only 'filename'\n"
+ "\t-0, --null Use \\0 instead of \\n\n"
+ "\t-m, --modname Handle argument as module name instead of alias or filename\n"
+ "\t-F, --field=FIELD Print only provided FIELD\n"
+ "\t-k, --set-version=VERSION Use VERSION instead of `uname -r`\n"
+ "\t-b, --basedir=DIR Use DIR as filesystem root for " MODULE_DIRECTORY "\n"
+ "\t-V, --version Show version\n"
+ "\t-h, --help Show this help\n",
+ program_invocation_short_name);
+}
+
+static bool is_module_filename(const char *name)
+{
+ struct stat st;
+
+ if (stat(name, &st) == 0 && S_ISREG(st.st_mode) &&
+ path_ends_with_kmod_ext(name, strlen(name)))
+ return true;
+
+ return false;
+}
+
+static int do_modinfo(int argc, char *argv[])
+{
+ struct kmod_ctx *ctx;
+ char dirname_buf[PATH_MAX];
+ const char *dirname = NULL;
+ const char *kversion = NULL;
+ const char *root = NULL;
+ const char *null_config = NULL;
+ bool arg_is_modname = false;
+ int i, err;
+
+ for (;;) {
+ int c, idx = 0;
+ c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'a':
+ field = "author";
+ break;
+ case 'd':
+ field = "description";
+ break;
+ case 'l':
+ field = "license";
+ break;
+ case 'p':
+ field = "parm";
+ break;
+ case 'n':
+ field = "filename";
+ break;
+ case '0':
+ separator = '\0';
+ break;
+ case 'm':
+ arg_is_modname = true;
+ break;
+ case 'F':
+ field = optarg;
+ break;
+ case 'k':
+ kversion = optarg;
+ break;
+ case 'b':
+ root = optarg;
+ break;
+ case 'h':
+ help();
+ return EXIT_SUCCESS;
+ case 'V':
+ puts(PACKAGE " version " VERSION);
+ puts(KMOD_FEATURES);
+ return EXIT_SUCCESS;
+ case '?':
+ return EXIT_FAILURE;
+ default:
+ ERR("unexpected getopt_long() value '%c'.\n", c);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind >= argc) {
+ ERR("missing module or filename.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (root != NULL || kversion != NULL) {
+ struct utsname u;
+ if (root == NULL)
+ root = "";
+ if (kversion == NULL) {
+ if (uname(&u) < 0) {
+ ERR("uname() failed: %m\n");
+ return EXIT_FAILURE;
+ }
+ kversion = u.release;
+ }
+ snprintf(dirname_buf, sizeof(dirname_buf), "%s" MODULE_DIRECTORY "/%s",
+ root, kversion);
+ dirname = dirname_buf;
+ }
+
+ ctx = kmod_new(dirname, &null_config);
+ if (!ctx) {
+ ERR("kmod_new() failed!\n");
+ return EXIT_FAILURE;
+ }
+
+ err = 0;
+ for (i = optind; i < argc; i++) {
+ const char *name = argv[i];
+ int r;
+
+ if (arg_is_modname)
+ r = modinfo_name_do(ctx, name);
+ else if (is_module_filename(name))
+ r = modinfo_path_do(ctx, name);
+ else
+ r = modinfo_alias_do(ctx, name);
+
+ if (r < 0)
+ err = r;
+ }
+
+ kmod_unref(ctx);
+ return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+const struct kmod_cmd kmod_cmd_compat_modinfo = {
+ .name = "modinfo",
+ .cmd = do_modinfo,
+ .help = "compat modinfo command",
+};
diff --git a/tools/modprobe.c b/tools/modprobe.c
new file mode 100644
index 0000000..5306bef
--- /dev/null
+++ b/tools/modprobe.c
@@ -0,0 +1,1054 @@
+/*
+ * kmod-modprobe - manage linux kernel modules using libkmod.
+ *
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+
+#include <shared/array.h>
+#include <shared/util.h>
+#include <shared/macro.h>
+
+#include <libkmod/libkmod.h>
+
+#include "kmod.h"
+
+static int log_priority = LOG_CRIT;
+static int use_syslog = 0;
+#define LOG(...) log_printf(log_priority, __VA_ARGS__)
+
+#define DEFAULT_VERBOSE LOG_WARNING
+static int verbose = DEFAULT_VERBOSE;
+static int do_show = 0;
+static int dry_run = 0;
+static int ignore_loaded = 0;
+static int lookup_only = 0;
+static int first_time = 0;
+static int ignore_commands = 0;
+static int use_blacklist = 0;
+static int force = 0;
+static int strip_modversion = 0;
+static int strip_vermagic = 0;
+static int remove_holders = 0;
+static unsigned long long wait_msec = 0;
+static int quiet_inuse = 0;
+
+static const char cmdopts_s[] = "arw:RibfDcnC:d:S:sqvVh";
+static const struct option cmdopts[] = {
+ {"all", no_argument, 0, 'a'},
+
+ {"remove", no_argument, 0, 'r'},
+ {"remove-dependencies", no_argument, 0, 5},
+ {"remove-holders", no_argument, 0, 5},
+ {"wait", required_argument, 0, 'w'},
+
+ {"resolve-alias", no_argument, 0, 'R'},
+ {"first-time", no_argument, 0, 3},
+ {"ignore-install", no_argument, 0, 'i'},
+ {"ignore-remove", no_argument, 0, 'i'},
+ {"use-blacklist", no_argument, 0, 'b'},
+ {"force", no_argument, 0, 'f'},
+ {"force-modversion", no_argument, 0, 2},
+ {"force-vermagic", no_argument, 0, 1},
+
+ {"show-depends", no_argument, 0, 'D'},
+ {"showconfig", no_argument, 0, 'c'},
+ {"show-config", no_argument, 0, 'c'},
+ {"show-modversions", no_argument, 0, 4},
+ {"dump-modversions", no_argument, 0, 4},
+ {"show-exports", no_argument, 0, 6},
+
+ {"dry-run", no_argument, 0, 'n'},
+ {"show", no_argument, 0, 'n'},
+
+ {"config", required_argument, 0, 'C'},
+ {"dirname", required_argument, 0, 'd'},
+ {"set-version", required_argument, 0, 'S'},
+
+ {"syslog", no_argument, 0, 's'},
+ {"quiet", no_argument, 0, 'q'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {NULL, 0, 0, 0}
+};
+
+static void help(void)
+{
+ printf("Usage:\n"
+ "\t%s [options] [-i] [-b] modulename\n"
+ "\t%s [options] -a [-i] [-b] modulename [modulename...]\n"
+ "\t%s [options] -r [-i] modulename\n"
+ "\t%s [options] -r -a [-i] modulename [modulename...]\n"
+ "\t%s [options] -c\n"
+ "\t%s [options] --dump-modversions filename\n"
+ "Management Options:\n"
+ "\t-a, --all Consider every non-argument to\n"
+ "\t be a module name to be inserted\n"
+ "\t or removed (-r)\n"
+ "\t-r, --remove Remove modules instead of inserting\n"
+ "\t --remove-dependencies Deprecated: use --remove-holders\n"
+ "\t --remove-holders Also remove module holders (use together with -r)\n"
+ "\t-w, --wait <MSEC> When removing a module, wait up to MSEC for\n"
+ "\t module's refcount to become 0 so it can be\n"
+ "\t removed (use together with -r)\n"
+ "\t --first-time Fail if module already inserted or removed\n"
+ "\t-i, --ignore-install Ignore install commands\n"
+ "\t-i, --ignore-remove Ignore remove commands\n"
+ "\t-b, --use-blacklist Apply blacklist to resolved alias.\n"
+ "\t-f, --force Force module insertion or removal.\n"
+ "\t implies --force-modversions and\n"
+ "\t --force-vermagic\n"
+ "\t --force-modversion Ignore module's version\n"
+ "\t --force-vermagic Ignore module's version magic\n"
+ "\n"
+ "Query Options:\n"
+ "\t-R, --resolve-alias Only lookup and print alias and exit\n"
+ "\t-D, --show-depends Only print module dependencies and exit\n"
+ "\t-c, --showconfig Print out known configuration and exit\n"
+ "\t-c, --show-config Same as --showconfig\n"
+ "\t --show-modversions Dump module symbol version and exit\n"
+ "\t --dump-modversions Same as --show-modversions\n"
+ "\t --show-exports Only print module exported symbol versions and exit\n"
+ "\n"
+ "General Options:\n"
+ "\t-n, --dry-run Do not execute operations, just print out\n"
+ "\t-n, --show Same as --dry-run\n"
+
+ "\t-C, --config=FILE Use FILE instead of default search paths\n"
+ "\t-d, --dirname=DIR Use DIR as filesystem root for " MODULE_DIRECTORY "\n"
+ "\t-S, --set-version=VERSION Use VERSION instead of `uname -r`\n"
+
+ "\t-s, --syslog print to syslog, not stderr\n"
+ "\t-q, --quiet disable messages\n"
+ "\t-v, --verbose enables more messages\n"
+ "\t-V, --version show version\n"
+ "\t-h, --help show this help\n",
+ program_invocation_short_name, program_invocation_short_name,
+ program_invocation_short_name, program_invocation_short_name,
+ program_invocation_short_name, program_invocation_short_name);
+}
+
+_printf_format_(1, 2)
+static inline void _show(const char *fmt, ...)
+{
+ va_list args;
+
+ if (!do_show && verbose <= DEFAULT_VERBOSE)
+ return;
+
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ fflush(stdout);
+ va_end(args);
+}
+#define SHOW(...) _show(__VA_ARGS__)
+
+static int show_config(struct kmod_ctx *ctx)
+{
+ struct config_iterators {
+ const char *name;
+ struct kmod_config_iter *(*get_iter)(const struct kmod_ctx *ctx);
+ } ci[] = {
+ { "blacklist", kmod_config_get_blacklists },
+ { "install", kmod_config_get_install_commands },
+ { "remove", kmod_config_get_remove_commands },
+ { "alias", kmod_config_get_aliases },
+ { "options", kmod_config_get_options },
+ { "softdep", kmod_config_get_softdeps },
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(ci); i++) {
+ struct kmod_config_iter *iter = ci[i].get_iter(ctx);
+
+ if (iter == NULL)
+ continue;
+
+ while (kmod_config_iter_next(iter)) {
+ const char *val;
+
+ printf("%s %s", ci[i].name,
+ kmod_config_iter_get_key(iter));
+ val = kmod_config_iter_get_value(iter);
+ if (val != NULL) {
+ putchar(' ');
+ puts(val);
+ } else
+ putchar('\n');
+ }
+
+ kmod_config_iter_free_iter(iter);
+ }
+
+ puts("\n# End of configuration files. Dumping indexes now:\n");
+ fflush(stdout);
+
+ kmod_dump_index(ctx, KMOD_INDEX_MODULES_ALIAS, STDOUT_FILENO);
+ kmod_dump_index(ctx, KMOD_INDEX_MODULES_SYMBOL, STDOUT_FILENO);
+
+ return 0;
+}
+
+static int show_modversions(struct kmod_ctx *ctx, const char *filename)
+{
+ struct kmod_list *l, *list = NULL;
+ struct kmod_module *mod;
+ int err = kmod_module_new_from_path(ctx, filename, &mod);
+ if (err < 0) {
+ LOG("Module %s not found.\n", filename);
+ return err;
+ }
+
+ err = kmod_module_get_versions(mod, &list);
+ if (err < 0) {
+ LOG("could not get modversions of %s: %s\n",
+ filename, strerror(-err));
+ kmod_module_unref(mod);
+ return err;
+ }
+
+ kmod_list_foreach(l, list) {
+ const char *symbol = kmod_module_version_get_symbol(l);
+ uint64_t crc = kmod_module_version_get_crc(l);
+ printf("0x%08"PRIx64"\t%s\n", crc, symbol);
+ }
+ kmod_module_versions_free_list(list);
+ kmod_module_unref(mod);
+ return 0;
+}
+
+static int show_exports(struct kmod_ctx *ctx, const char *filename)
+{
+ struct kmod_list *l, *list = NULL;
+ struct kmod_module *mod;
+ int err = kmod_module_new_from_path(ctx, filename, &mod);
+ if (err < 0) {
+ LOG("Module %s not found.\n", filename);
+ return err;
+ }
+
+ err = kmod_module_get_symbols(mod, &list);
+ if (err < 0) {
+ LOG("could not get symbols of %s: %s\n",
+ filename, strerror(-err));
+ kmod_module_unref(mod);
+ return err;
+ }
+
+ kmod_list_foreach(l, list) {
+ const char *symbol = kmod_module_symbol_get_symbol(l);
+ uint64_t crc = kmod_module_symbol_get_crc(l);
+ printf("0x%08"PRIx64"\t%s\n", crc, symbol);
+ }
+ kmod_module_symbols_free_list(list);
+ kmod_module_unref(mod);
+ return 0;
+}
+
+static int command_do(struct kmod_module *module, const char *type,
+ const char *command, const char *cmdline_opts)
+{
+ const char *modname = kmod_module_get_name(module);
+ char *p, *cmd = NULL;
+ size_t cmdlen, cmdline_opts_len, varlen;
+ int ret = 0;
+
+ if (cmdline_opts == NULL)
+ cmdline_opts = "";
+ cmdline_opts_len = strlen(cmdline_opts);
+
+ cmd = strdup(command);
+ if (cmd == NULL)
+ return -ENOMEM;
+ cmdlen = strlen(cmd);
+ varlen = sizeof("$CMDLINE_OPTS") - 1;
+ while ((p = strstr(cmd, "$CMDLINE_OPTS")) != NULL) {
+ size_t prefixlen = p - cmd;
+ size_t suffixlen = cmdlen - prefixlen - varlen;
+ size_t slen = cmdlen - varlen + cmdline_opts_len;
+ char *suffix = p + varlen;
+ char *s = malloc(slen + 1);
+ if (s == NULL) {
+ free(cmd);
+ return -ENOMEM;
+ }
+ memcpy(s, cmd, p - cmd);
+ memcpy(s + prefixlen, cmdline_opts, cmdline_opts_len);
+ memcpy(s + prefixlen + cmdline_opts_len, suffix, suffixlen);
+ s[slen] = '\0';
+
+ free(cmd);
+ cmd = s;
+ cmdlen = slen;
+ }
+
+ SHOW("%s %s\n", type, cmd);
+ if (dry_run)
+ goto end;
+
+ setenv("MODPROBE_MODULE", modname, 1);
+ ret = system(cmd);
+ unsetenv("MODPROBE_MODULE");
+ if (ret == -1 || WEXITSTATUS(ret)) {
+ LOG("Error running %s command for %s\n", type, modname);
+ if (ret != -1)
+ ret = -WEXITSTATUS(ret);
+ }
+
+end:
+ free(cmd);
+ return ret;
+}
+
+static int rmmod_do_remove_module(struct kmod_module *mod)
+{
+ const char *modname = kmod_module_get_name(mod);
+ unsigned long long interval_msec = 0, t0_msec = 0,
+ tend_msec = 0;
+ int flags = 0, err;
+
+ SHOW("rmmod %s\n", modname);
+
+ if (dry_run)
+ return 0;
+
+ if (force)
+ flags |= KMOD_REMOVE_FORCE;
+
+ if (wait_msec)
+ flags |= KMOD_REMOVE_NOLOG;
+
+ do {
+ err = kmod_module_remove_module(mod, flags);
+ if (err == -EEXIST) {
+ if (!first_time)
+ err = 0;
+ else
+ LOG("Module %s is not in kernel.\n", modname);
+ break;
+ } else if (err == -EAGAIN && wait_msec) {
+ unsigned long long until_msec;
+
+ if (!t0_msec) {
+ t0_msec = now_msec();
+ tend_msec = t0_msec + wait_msec;
+ interval_msec = 1;
+ }
+
+ until_msec = get_backoff_delta_msec(t0_msec, tend_msec,
+ &interval_msec);
+ err = sleep_until_msec(until_msec);
+
+ if (!t0_msec)
+ err = -ENOTSUP;
+
+ if (err < 0) {
+ ERR("Failed to sleep: %s\n", strerror(-err));
+ err = -EAGAIN;
+ break;
+ }
+ } else {
+ break;
+ }
+ } while (interval_msec);
+
+ if (err < 0 && wait_msec)
+ ERR("could not remove '%s': %s\n", modname, strerror(-err));
+
+ return err;
+}
+
+#define RMMOD_FLAG_REMOVE_HOLDERS 0x1
+#define RMMOD_FLAG_IGNORE_BUILTIN 0x2
+static int rmmod_do_module(struct kmod_module *mod, int flags);
+
+/* Remove modules in reverse order */
+static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors)
+{
+ struct kmod_list *l;
+
+ kmod_list_foreach_reverse(l, list) {
+ struct kmod_module *m = kmod_module_get_module(l);
+ int r = rmmod_do_module(m, RMMOD_FLAG_IGNORE_BUILTIN);
+ kmod_module_unref(m);
+
+ if (r < 0 && stop_on_errors)
+ return r;
+ }
+
+ return 0;
+}
+
+static int rmmod_do_module(struct kmod_module *mod, int flags)
+{
+ const char *modname = kmod_module_get_name(mod);
+ struct kmod_list *pre = NULL, *post = NULL;
+ const char *cmd = NULL;
+ int err;
+
+ if (!ignore_commands) {
+ err = kmod_module_get_softdeps(mod, &pre, &post);
+ if (err < 0) {
+ WRN("could not get softdeps of '%s': %s\n",
+ modname, strerror(-err));
+ return err;
+ }
+
+ cmd = kmod_module_get_remove_commands(mod);
+ }
+
+ /* Quick check if module is loaded, otherwise there's nothing to do */
+ if (!cmd && !ignore_loaded) {
+ int state = kmod_module_get_initstate(mod);
+
+ if (state < 0) {
+ if (first_time) {
+ LOG("Module %s is not in kernel.\n", modname);
+ err = -ENOENT;
+ } else {
+ err = 0;
+ }
+ goto error;
+ } else if (state == KMOD_MODULE_BUILTIN) {
+ if (flags & RMMOD_FLAG_IGNORE_BUILTIN) {
+ err = 0;
+ } else {
+ LOG("Module %s is builtin.\n", modname);
+ err = -ENOENT;
+ }
+ goto error;
+ }
+ }
+
+ /* 1. @mod's post-softdeps in reverse order */
+ rmmod_do_modlist(post, false);
+
+ /* 2. Other modules holding @mod */
+ if (flags & RMMOD_FLAG_REMOVE_HOLDERS) {
+ struct kmod_list *holders = kmod_module_get_holders(mod);
+
+ err = rmmod_do_modlist(holders, true);
+ kmod_module_unref_list(holders);
+ if (err < 0)
+ goto error;
+ }
+
+ /* 3. @mod itself, but check for refcnt first */
+ if (!cmd && !ignore_loaded && !wait_msec) {
+ int usage = kmod_module_get_refcnt(mod);
+
+ if (usage > 0) {
+ if (!quiet_inuse)
+ LOG("Module %s is in use.\n", modname);
+
+ err = -EBUSY;
+ goto error;
+ }
+ }
+
+ if (!cmd)
+ err = rmmod_do_remove_module(mod);
+ else
+ err = command_do(mod, "remove", cmd, NULL);
+
+ if (err < 0)
+ goto error;
+
+ /* 4. Other modules that became unused: errors are non-fatal */
+ if (!cmd) {
+ struct kmod_list *deps, *itr;
+
+ deps = kmod_module_get_dependencies(mod);
+ kmod_list_foreach(itr, deps) {
+ struct kmod_module *dep = kmod_module_get_module(itr);
+ if (kmod_module_get_refcnt(dep) == 0)
+ rmmod_do_remove_module(dep);
+ kmod_module_unref(dep);
+ }
+ kmod_module_unref_list(deps);
+ }
+
+ /* 5. @mod's pre-softdeps in reverse order: errors are non-fatal */
+ rmmod_do_modlist(pre, false);
+
+error:
+ kmod_module_unref_list(pre);
+ kmod_module_unref_list(post);
+
+ return err;
+}
+
+static int rmmod(struct kmod_ctx *ctx, const char *alias)
+{
+ struct kmod_list *l, *list = NULL;
+ int err;
+
+ err = kmod_module_new_from_lookup(ctx, alias, &list);
+ if (err < 0)
+ return err;
+
+ if (list == NULL) {
+ LOG("Module %s not found.\n", alias);
+ err = -ENOENT;
+ }
+
+ kmod_list_foreach(l, list) {
+ struct kmod_module *mod = kmod_module_get_module(l);
+ int flags = remove_holders ? RMMOD_FLAG_REMOVE_HOLDERS : 0;
+
+ err = rmmod_do_module(mod, flags);
+ kmod_module_unref(mod);
+ if (err < 0)
+ break;
+ }
+
+ kmod_module_unref_list(list);
+ return err;
+}
+
+static int rmmod_all(struct kmod_ctx *ctx, char **args, int nargs)
+{
+ int i, err = 0;
+
+ for (i = 0; i < nargs; i++) {
+ int r = rmmod(ctx, args[i]);
+ if (r < 0)
+ err = r;
+ }
+
+ return err;
+}
+
+static void print_action(struct kmod_module *m, bool install,
+ const char *options)
+{
+ const char *path;
+
+ if (install) {
+ printf("install %s %s\n", kmod_module_get_install_commands(m),
+ options);
+ return;
+ }
+
+ path = kmod_module_get_path(m);
+
+ if (path == NULL) {
+ /*
+ * Either a builtin module, or an alias, print only for
+ * builtin
+ */
+ if (kmod_module_get_initstate(m) == KMOD_MODULE_BUILTIN)
+ printf("builtin %s\n", kmod_module_get_name(m));
+ } else
+ printf("insmod %s %s\n", kmod_module_get_path(m), options);
+}
+
+static int insmod_insert(struct kmod_module *mod, int flags,
+ const char *extra_options)
+{
+ int err = 0;
+ void (*show)(struct kmod_module *m, bool install,
+ const char *options) = NULL;
+
+ if (do_show || verbose > DEFAULT_VERBOSE)
+ show = &print_action;
+
+ if (lookup_only)
+ printf("%s\n", kmod_module_get_name(mod));
+ else
+ err = kmod_module_probe_insert_module(mod, flags,
+ extra_options, NULL, NULL, show);
+
+ if (err >= 0)
+ /* ignore flag return values such as a mod being blacklisted */
+ err = 0;
+ else {
+ switch (err) {
+ case -EEXIST:
+ ERR("could not insert '%s': Module already in kernel\n",
+ kmod_module_get_name(mod));
+ break;
+ case -ENOENT:
+ ERR("could not insert '%s': Unknown symbol in module, "
+ "or unknown parameter (see dmesg)\n",
+ kmod_module_get_name(mod));
+ break;
+ default:
+ ERR("could not insert '%s': %s\n",
+ kmod_module_get_name(mod),
+ strerror(-err));
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int insmod(struct kmod_ctx *ctx, const char *alias,
+ const char *extra_options)
+{
+ struct kmod_list *l, *list = NULL;
+ struct kmod_module *mod = NULL;
+ int err, flags = 0;
+
+ if (strncmp(alias, "/", 1) == 0 || strncmp(alias, "./", 2) == 0) {
+ err = kmod_module_new_from_path(ctx, alias, &mod);
+ if (err < 0) {
+ LOG("Failed to get module from path %s: %s\n", alias,
+ strerror(-err));
+ return -ENOENT;
+ }
+ } else {
+ err = kmod_module_new_from_lookup(ctx, alias, &list);
+ if (list == NULL || err < 0) {
+ LOG("Module %s not found in directory %s\n", alias,
+ ctx ? kmod_get_dirname(ctx) : "(missing)");
+ return -ENOENT;
+ }
+ }
+
+ if (strip_modversion || force)
+ flags |= KMOD_PROBE_FORCE_MODVERSION;
+ if (strip_vermagic || force)
+ flags |= KMOD_PROBE_FORCE_VERMAGIC;
+ if (ignore_commands)
+ flags |= KMOD_PROBE_IGNORE_COMMAND;
+ if (ignore_loaded)
+ flags |= KMOD_PROBE_IGNORE_LOADED;
+ if (dry_run)
+ flags |= KMOD_PROBE_DRY_RUN;
+
+ flags |= KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY;
+
+ if (use_blacklist)
+ flags |= KMOD_PROBE_APPLY_BLACKLIST;
+ if (first_time)
+ flags |= KMOD_PROBE_FAIL_ON_LOADED;
+
+ /* If module is loaded from path */
+ if (mod != NULL) {
+ err = insmod_insert(mod, flags, extra_options);
+ kmod_module_unref(mod);
+ } else {
+ kmod_list_foreach(l, list) {
+ mod = kmod_module_get_module(l);
+ err = insmod_insert(mod, flags, extra_options);
+ kmod_module_unref(mod);
+ }
+ kmod_module_unref_list(list);
+ }
+ return err;
+}
+
+static int insmod_all(struct kmod_ctx *ctx, char **args, int nargs)
+{
+ int i, err = 0;
+
+ for (i = 0; i < nargs; i++) {
+ int r = insmod(ctx, args[i], NULL);
+ if (r < 0)
+ err = r;
+ }
+
+ return err;
+}
+
+static void env_modprobe_options_append(const char *value)
+{
+ const char *old = getenv("MODPROBE_OPTIONS");
+ char *env;
+
+ if (old == NULL) {
+ setenv("MODPROBE_OPTIONS", value, 1);
+ return;
+ }
+
+ if (asprintf(&env, "%s %s", old, value) < 0) {
+ ERR("could not append value to $MODPROBE_OPTIONS\n");
+ return;
+ }
+
+ if (setenv("MODPROBE_OPTIONS", env, 1) < 0)
+ ERR("could not setenv(MODPROBE_OPTIONS, \"%s\")\n", env);
+ free(env);
+}
+
+static int options_from_array(char **args, int nargs, char **output)
+{
+ char *opts = NULL;
+ size_t optslen = 0;
+ int i, err = 0;
+
+ for (i = 1; i < nargs; i++) {
+ size_t len = strlen(args[i]);
+ size_t qlen = 0;
+ const char *value;
+ void *tmp;
+
+ value = strchr(args[i], '=');
+ if (value) {
+ value++;
+ if (*value != '"' && *value != '\'') {
+ if (strchr(value, ' '))
+ qlen = 2;
+ }
+ }
+
+ tmp = realloc(opts, optslen + len + qlen + 2);
+ if (!tmp) {
+ err = -errno;
+ free(opts);
+ opts = NULL;
+ ERR("could not gather module options: out-of-memory\n");
+ break;
+ }
+ opts = tmp;
+ if (optslen > 0) {
+ opts[optslen] = ' ';
+ optslen++;
+ }
+ if (qlen == 0) {
+ memcpy(opts + optslen, args[i], len + 1);
+ optslen += len;
+ } else {
+ size_t keylen = value - args[i];
+ size_t valuelen = len - keylen;
+ memcpy(opts + optslen, args[i], keylen);
+ optslen += keylen;
+ opts[optslen] = '"';
+ optslen++;
+ memcpy(opts + optslen, value, valuelen);
+ optslen += valuelen;
+ opts[optslen] = '"';
+ optslen++;
+ opts[optslen] = '\0';
+ }
+ }
+
+ *output = opts;
+ return err;
+}
+
+static char **prepend_options_from_env(int *p_argc, char **orig_argv)
+{
+ const char *p, *env = getenv("MODPROBE_OPTIONS");
+ char **new_argv, *str_end, *str, *s, *quote;
+ int i, argc = *p_argc;
+ size_t envlen, space_count = 0;
+
+ if (env == NULL)
+ return orig_argv;
+
+ for (p = env; *p != '\0'; p++) {
+ if (*p == ' ')
+ space_count++;
+ }
+
+ envlen = p - env;
+ new_argv = malloc(sizeof(char *) * (argc + space_count + 3 + envlen));
+ if (new_argv == NULL)
+ return NULL;
+
+ new_argv[0] = orig_argv[0];
+ str = (char *) (new_argv + argc + space_count + 3);
+ memcpy(str, env, envlen + 1);
+
+ str_end = str + envlen;
+
+ quote = NULL;
+ for (i = 1, s = str; *s != '\0'; s++) {
+ if (quote == NULL) {
+ if (*s == ' ') {
+ new_argv[i] = str;
+ i++;
+ *s = '\0';
+ str = s + 1;
+ } else if (*s == '"' || *s == '\'')
+ quote = s;
+ } else {
+ if (*s == *quote) {
+ if (quote == str) {
+ new_argv[i] = str + 1;
+ i++;
+ *s = '\0';
+ str = s + 1;
+ } else {
+ char *it;
+ for (it = quote; it < s - 1; it++)
+ it[0] = it[1];
+ for (it = s - 1; it < str_end - 2; it++)
+ it[0] = it[2];
+ str_end -= 2;
+ *str_end = '\0';
+ s -= 2;
+ }
+ quote = NULL;
+ }
+ }
+ }
+ if (str < s) {
+ new_argv[i] = str;
+ i++;
+ }
+
+ memcpy(new_argv + i, orig_argv + 1, sizeof(char *) * (argc - 1));
+ new_argv[i + argc - 1] = NULL;
+ *p_argc = i + argc - 1;
+
+ return new_argv;
+}
+
+static int do_modprobe(int argc, char **orig_argv)
+{
+ struct kmod_ctx *ctx;
+ char **args = NULL, **argv;
+ const char **config_paths = NULL;
+ int nargs = 0, n_config_paths = 0;
+ char dirname_buf[PATH_MAX];
+ const char *dirname = NULL;
+ const char *root = NULL;
+ const char *kversion = NULL;
+ int use_all = 0;
+ int do_remove = 0;
+ int do_show_config = 0;
+ int do_show_modversions = 0;
+ int do_show_exports = 0;
+ int err;
+ struct stat stat_buf;
+
+ argv = prepend_options_from_env(&argc, orig_argv);
+ if (argv == NULL) {
+ ERR("Could not prepend options from command line\n");
+ return EXIT_FAILURE;
+ }
+
+ for (;;) {
+ int c, idx = 0;
+ c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'a':
+ log_priority = LOG_WARNING;
+ use_all = 1;
+ break;
+ case 'r':
+ do_remove = 1;
+ break;
+ case 5:
+ remove_holders = 1;
+ break;
+ case 'w': {
+ char *endptr = NULL;
+ wait_msec = strtoul(optarg, &endptr, 0);
+ if (!*optarg || *endptr) {
+ ERR("unexpected wait value '%s'.\n", optarg);
+ err = -1;
+ goto done;
+ }
+ break;
+ }
+ case 3:
+ first_time = 1;
+ break;
+ case 'i':
+ ignore_commands = 1;
+ break;
+ case 'b':
+ use_blacklist = 1;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 2:
+ strip_modversion = 1;
+ break;
+ case 1:
+ strip_vermagic = 1;
+ break;
+ case 'D':
+ ignore_loaded = 1;
+ dry_run = 1;
+ do_show = 1;
+ break;
+ case 'R':
+ lookup_only = 1;
+ break;
+ case 'c':
+ do_show_config = 1;
+ break;
+ case 4:
+ do_show_modversions = 1;
+ break;
+ case 6:
+ do_show_exports = 1;
+ break;
+ case 'n':
+ dry_run = 1;
+ break;
+ case 'C': {
+ size_t bytes = sizeof(char *) * (n_config_paths + 2);
+ void *tmp = realloc(config_paths, bytes);
+ if (!tmp) {
+ ERR("out-of-memory\n");
+ err = -1;
+ goto done;
+ }
+ config_paths = tmp;
+ config_paths[n_config_paths] = optarg;
+ n_config_paths++;
+ config_paths[n_config_paths] = NULL;
+
+ env_modprobe_options_append("-C");
+ env_modprobe_options_append(optarg);
+ break;
+ }
+ case 'd':
+ root = optarg;
+ break;
+ case 'S':
+ kversion = optarg;
+ break;
+ case 's':
+ env_modprobe_options_append("-s");
+ use_syslog = 1;
+ break;
+ case 'q':
+ env_modprobe_options_append("-q");
+ verbose = LOG_EMERG;
+ break;
+ case 'v':
+ env_modprobe_options_append("-v");
+ verbose++;
+ break;
+ case 'V':
+ puts(PACKAGE " version " VERSION);
+ puts(KMOD_FEATURES);
+ err = 0;
+ goto done;
+ case 'h':
+ help();
+ err = 0;
+ goto done;
+ case '?':
+ err = -1;
+ goto done;
+ default:
+ ERR("unexpected getopt_long() value '%c'.\n", c);
+ err = -1;
+ goto done;
+ }
+ }
+
+ args = argv + optind;
+ nargs = argc - optind;
+
+ if (!use_syslog &&
+ (!stderr ||
+ fileno(stderr) == -1 ||
+ fstat(fileno(stderr), &stat_buf)))
+ use_syslog = 1;
+
+ log_open(use_syslog);
+
+ if (!do_show_config) {
+ if (nargs == 0) {
+ ERR("missing parameters. See -h.\n");
+ err = -1;
+ goto done;
+ }
+ }
+
+ if (root != NULL || kversion != NULL) {
+ struct utsname u;
+ if (root == NULL)
+ root = "";
+ if (kversion == NULL) {
+ if (uname(&u) < 0) {
+ ERR("uname() failed: %m\n");
+ err = -1;
+ goto done;
+ }
+ kversion = u.release;
+ }
+ snprintf(dirname_buf, sizeof(dirname_buf),
+ "%s" MODULE_DIRECTORY "/%s", root,
+ kversion);
+ dirname = dirname_buf;
+ }
+
+ ctx = kmod_new(dirname, config_paths);
+ if (!ctx) {
+ ERR("kmod_new() failed!\n");
+ err = -1;
+ goto done;
+ }
+
+ log_setup_kmod_log(ctx, verbose);
+
+ kmod_load_resources(ctx);
+
+ if (do_show_config)
+ err = show_config(ctx);
+ else if (do_show_modversions)
+ err = show_modversions(ctx, args[0]);
+ else if (do_show_exports)
+ err = show_exports(ctx, args[0]);
+ else if (do_remove)
+ err = rmmod_all(ctx, args, nargs);
+ else if (use_all)
+ err = insmod_all(ctx, args, nargs);
+ else {
+ char *opts;
+ err = options_from_array(args, nargs, &opts);
+ if (err == 0) {
+ err = insmod(ctx, args[0], opts);
+ free(opts);
+ }
+ }
+
+ kmod_unref(ctx);
+
+done:
+ log_close();
+
+ if (argv != orig_argv)
+ free(argv);
+
+ free(config_paths);
+
+ return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+const struct kmod_cmd kmod_cmd_compat_modprobe = {
+ .name = "modprobe",
+ .cmd = do_modprobe,
+ .help = "compat modprobe command",
+};
diff --git a/tools/remove.c b/tools/remove.c
new file mode 100644
index 0000000..387ef0e
--- /dev/null
+++ b/tools/remove.c
@@ -0,0 +1,153 @@
+/*
+ * kmod-remove - remove modules from the kernel.
+ *
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libkmod/libkmod.h>
+
+#include "kmod.h"
+
+static const char cmdopts_s[] = "h";
+static const struct option cmdopts[] = {
+ {"help", no_argument, 0, 'h'},
+ { }
+};
+
+static void help(void)
+{
+ printf("Usage:\n"
+ "\t%s remove [options] module\n"
+ "Options:\n"
+ "\t-h, --help show this help\n",
+ program_invocation_short_name);
+}
+
+static int check_module_inuse(struct kmod_module *mod) {
+ struct kmod_list *holders;
+ int state, ret;
+
+ state = kmod_module_get_initstate(mod);
+
+ if (state == KMOD_MODULE_BUILTIN) {
+ ERR("Module %s is builtin.\n", kmod_module_get_name(mod));
+ return -ENOENT;
+ } else if (state < 0) {
+ ERR("Module %s is not currently loaded\n",
+ kmod_module_get_name(mod));
+ return -ENOENT;
+ }
+
+ holders = kmod_module_get_holders(mod);
+ if (holders != NULL) {
+ struct kmod_list *itr;
+
+ ERR("Module %s is in use by:", kmod_module_get_name(mod));
+
+ kmod_list_foreach(itr, holders) {
+ struct kmod_module *hm = kmod_module_get_module(itr);
+ fprintf(stderr, " %s", kmod_module_get_name(hm));
+ kmod_module_unref(hm);
+ }
+ fputc('\n', stderr);
+
+ kmod_module_unref_list(holders);
+ return -EBUSY;
+ }
+
+ ret = kmod_module_get_refcnt(mod);
+ if (ret > 0) {
+ ERR("Module %s is in use\n", kmod_module_get_name(mod));
+ return -EBUSY;
+ } else if (ret == -ENOENT) {
+ ERR("Module unloading is not supported\n");
+ }
+
+ return ret;
+}
+
+static int do_remove(int argc, char *argv[])
+{
+ struct kmod_ctx *ctx;
+ struct kmod_module *mod;
+ const char *name;
+ int err, r = EXIT_SUCCESS;
+
+ for (;;) {
+ int c, idx =0;
+ c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'h':
+ help();
+ return EXIT_SUCCESS;
+
+ default:
+ ERR("Unexpected getopt_long() value '%c'.\n", c);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind >= argc) {
+ ERR("Missing module name\n");
+ return EXIT_FAILURE;
+ }
+
+ ctx = kmod_new(NULL, NULL);
+ if (!ctx) {
+ ERR("kmod_new() failed!\n");
+ return EXIT_FAILURE;
+ }
+
+ name = argv[optind];
+ err = kmod_module_new_from_name(ctx, name, &mod);
+ if (err < 0) {
+ ERR("Could not remove module %s: %s\n", name, strerror(-err));
+ goto end;
+ }
+
+ err = check_module_inuse(mod);
+ if (err < 0)
+ goto unref;
+
+ err = kmod_module_remove_module(mod, 0);
+ if (err < 0)
+ goto unref;
+
+unref:
+ kmod_module_unref(mod);
+
+end:
+ kmod_unref(ctx);
+ if (err < 0) {
+ r = EXIT_FAILURE;
+ ERR("Could not remove module %s: %s\n", name, strerror(-err));
+ }
+ return r;
+}
+
+const struct kmod_cmd kmod_cmd_remove = {
+ .name = "remove",
+ .cmd = do_remove,
+ .help = "remove module from kernel",
+};
diff --git a/tools/rmmod.c b/tools/rmmod.c
new file mode 100644
index 0000000..3942e7b
--- /dev/null
+++ b/tools/rmmod.c
@@ -0,0 +1,203 @@
+/*
+ * kmod-rmmod - remove modules from linux kernel using libkmod.
+ *
+ * Copyright (C) 2011-2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <shared/macro.h>
+
+#include <libkmod/libkmod.h>
+
+#include "kmod.h"
+
+#define DEFAULT_VERBOSE LOG_ERR
+static int verbose = DEFAULT_VERBOSE;
+static int use_syslog;
+
+static const char cmdopts_s[] = "fsvVwh";
+static const struct option cmdopts[] = {
+ {"force", no_argument, 0, 'f'},
+ {"syslog", no_argument, 0, 's'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {NULL, 0, 0, 0}
+};
+
+static void help(void)
+{
+ printf("Usage:\n"
+ "\t%s [options] modulename ...\n"
+ "Options:\n"
+ "\t-f, --force forces a module unload and may crash your\n"
+ "\t machine. This requires Forced Module Removal\n"
+ "\t option in your kernel. DANGEROUS\n"
+ "\t-s, --syslog print to syslog, not stderr\n"
+ "\t-v, --verbose enables more messages\n"
+ "\t-V, --version show version\n"
+ "\t-h, --help show this help\n",
+ program_invocation_short_name);
+}
+
+static int check_module_inuse(struct kmod_module *mod) {
+ struct kmod_list *holders;
+ int state, ret;
+
+ state = kmod_module_get_initstate(mod);
+
+ if (state == KMOD_MODULE_BUILTIN) {
+ ERR("Module %s is builtin.\n", kmod_module_get_name(mod));
+ return -ENOENT;
+ } else if (state < 0) {
+ ERR("Module %s is not currently loaded\n",
+ kmod_module_get_name(mod));
+ return -ENOENT;
+ }
+
+ holders = kmod_module_get_holders(mod);
+ if (holders != NULL) {
+ struct kmod_list *itr;
+
+ ERR("Module %s is in use by:", kmod_module_get_name(mod));
+
+ kmod_list_foreach(itr, holders) {
+ struct kmod_module *hm = kmod_module_get_module(itr);
+ fprintf(stderr, " %s", kmod_module_get_name(hm));
+ kmod_module_unref(hm);
+ }
+ fputc('\n', stderr);
+
+ kmod_module_unref_list(holders);
+ return -EBUSY;
+ }
+
+ ret = kmod_module_get_refcnt(mod);
+ if (ret > 0) {
+ ERR("Module %s is in use\n", kmod_module_get_name(mod));
+ return -EBUSY;
+ } else if (ret == -ENOENT) {
+ ERR("Module unloading is not supported\n");
+ }
+
+ return ret;
+}
+
+static int do_rmmod(int argc, char *argv[])
+{
+ struct kmod_ctx *ctx;
+ const char *null_config = NULL;
+ int flags = 0;
+ int i, err, r = 0;
+
+ for (;;) {
+ int c, idx = 0;
+ c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'f':
+ flags |= KMOD_REMOVE_FORCE;
+ break;
+ case 's':
+ use_syslog = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'h':
+ help();
+ return EXIT_SUCCESS;
+ case 'V':
+ puts(PACKAGE " version " VERSION);
+ puts(KMOD_FEATURES);
+ return EXIT_SUCCESS;
+ case '?':
+ return EXIT_FAILURE;
+ default:
+ ERR("unexpected getopt_long() value '%c'.\n", c);
+ return EXIT_FAILURE;
+ }
+ }
+
+ log_open(use_syslog);
+
+ if (optind >= argc) {
+ ERR("missing module name.\n");
+ r = EXIT_FAILURE;
+ goto done;
+ }
+
+ ctx = kmod_new(NULL, &null_config);
+ if (!ctx) {
+ ERR("kmod_new() failed!\n");
+ r = EXIT_FAILURE;
+ goto done;
+ }
+
+ log_setup_kmod_log(ctx, verbose);
+
+ for (i = optind; i < argc; i++) {
+ struct kmod_module *mod;
+ const char *arg = argv[i];
+ struct stat st;
+ if (stat(arg, &st) == 0)
+ err = kmod_module_new_from_path(ctx, arg, &mod);
+ else
+ err = kmod_module_new_from_name(ctx, arg, &mod);
+
+ if (err < 0) {
+ ERR("could not use module %s: %s\n", arg,
+ strerror(-err));
+ break;
+ }
+
+ if (!(flags & KMOD_REMOVE_FORCE) && check_module_inuse(mod) < 0) {
+ r++;
+ goto next;
+ }
+
+ err = kmod_module_remove_module(mod, flags);
+ if (err < 0) {
+ ERR("could not remove module %s: %s\n", arg,
+ strerror(-err));
+ r++;
+ }
+next:
+ kmod_module_unref(mod);
+ }
+
+ kmod_unref(ctx);
+
+done:
+ log_close();
+
+ return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+const struct kmod_cmd kmod_cmd_compat_rmmod = {
+ .name = "rmmod",
+ .cmd = do_rmmod,
+ .help = "compat rmmod command",
+};
diff --git a/tools/static-nodes.c b/tools/static-nodes.c
new file mode 100644
index 0000000..5ef3743
--- /dev/null
+++ b/tools/static-nodes.c
@@ -0,0 +1,277 @@
+/*
+ * kmod-static-nodes - manage modules.devname
+ *
+ * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ * Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#include <shared/util.h>
+
+#include "kmod.h"
+
+struct static_nodes_format {
+ const char *name;
+ int (*write)(FILE *, char[], char[], char, unsigned int, unsigned int);
+ const char *description;
+};
+
+static const struct static_nodes_format static_nodes_format_human;
+static const struct static_nodes_format static_nodes_format_tmpfiles;
+static const struct static_nodes_format static_nodes_format_devname;
+
+static const struct static_nodes_format *static_nodes_formats[] = {
+ &static_nodes_format_human,
+ &static_nodes_format_tmpfiles,
+ &static_nodes_format_devname,
+};
+
+static const char cmdopts_s[] = "o:f:h";
+static const struct option cmdopts[] = {
+ { "output", required_argument, 0, 'o'},
+ { "format", required_argument, 0, 'f'},
+ { "help", no_argument, 0, 'h'},
+ { },
+};
+
+static int write_human(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min)
+{
+ int ret;
+
+ ret = fprintf(out,
+ "Module: %s\n"
+ "\tDevice node: /dev/%s\n"
+ "\t\tType: %s device\n"
+ "\t\tMajor: %u\n"
+ "\t\tMinor: %u\n",
+ modname, devname,
+ (type == 'c') ? "character" : "block", maj, min);
+ if (ret >= 0)
+ return EXIT_SUCCESS;
+ else
+ return EXIT_FAILURE;
+}
+
+static const struct static_nodes_format static_nodes_format_human = {
+ .name = "human",
+ .write = write_human,
+ .description = "(default) a human readable format. Do not parse.",
+};
+
+static int write_tmpfiles(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min)
+{
+ const char *dir;
+ int ret;
+
+ dir = strrchr(devname, '/');
+ if (dir) {
+ ret = fprintf(out, "d /dev/%.*s 0755 - - -\n",
+ (int)(dir - devname), devname);
+ if (ret < 0)
+ return EXIT_FAILURE;
+ }
+
+ ret = fprintf(out, "%c! /dev/%s 0600 - - - %u:%u\n",
+ type, devname, maj, min);
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+
+static const struct static_nodes_format static_nodes_format_tmpfiles = {
+ .name = "tmpfiles",
+ .write = write_tmpfiles,
+ .description = "the tmpfiles.d(5) format used by systemd-tmpfiles.",
+};
+
+static int write_devname(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min)
+{
+ int ret;
+
+ ret = fprintf(out, "%s %s %c%u:%u\n", modname, devname, type, maj, min);
+ if (ret >= 0)
+ return EXIT_SUCCESS;
+ else
+ return EXIT_FAILURE;
+}
+
+static const struct static_nodes_format static_nodes_format_devname = {
+ .name = "devname",
+ .write = write_devname,
+ .description = "the modules.devname format.",
+};
+
+static void help(void)
+{
+ size_t i;
+
+ printf("Usage:\n"
+ "\t%s static-nodes [options]\n"
+ "\n"
+ "kmod static-nodes outputs the static-node information of the currently running kernel.\n"
+ "\n"
+ "Options:\n"
+ "\t-f, --format=FORMAT choose format to use: see \"Formats\"\n"
+ "\t-o, --output=FILE write output to file\n"
+ "\t-h, --help show this help\n"
+ "\n"
+ "Formats:\n",
+ program_invocation_short_name);
+
+ for (i = 0; i < ARRAY_SIZE(static_nodes_formats); i++) {
+ if (static_nodes_formats[i]->description != NULL) {
+ printf("\t%-12s %s\n", static_nodes_formats[i]->name,
+ static_nodes_formats[i]->description);
+ }
+ }
+}
+
+static int do_static_nodes(int argc, char *argv[])
+{
+ struct utsname kernel;
+ char modules[PATH_MAX], buf[4096];
+ const char *output = "/dev/stdout";
+ FILE *in = NULL, *out = NULL;
+ const struct static_nodes_format *format = &static_nodes_format_human;
+ int r, ret = EXIT_SUCCESS;
+
+ for (;;) {
+ int c, idx = 0, valid;
+ size_t i;
+
+ c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case 'o':
+ output = optarg;
+ break;
+ case 'f':
+ valid = 0;
+
+ for (i = 0; i < ARRAY_SIZE(static_nodes_formats); i++) {
+ if (streq(static_nodes_formats[i]->name, optarg)) {
+ format = static_nodes_formats[i];
+ valid = 1;
+ }
+ }
+
+ if (!valid) {
+ fprintf(stderr, "Unknown format: '%s'.\n",
+ optarg);
+ help();
+ ret = EXIT_FAILURE;
+ goto finish;
+ }
+ break;
+ case 'h':
+ help();
+ goto finish;
+ case '?':
+ ret = EXIT_FAILURE;
+ goto finish;
+ default:
+ fprintf(stderr, "Unexpected commandline option '%c'.\n",
+ c);
+ help();
+ ret = EXIT_FAILURE;
+ goto finish;
+ }
+ }
+
+ if (uname(&kernel) < 0) {
+ fputs("Error: uname failed!\n", stderr);
+ ret = EXIT_FAILURE;
+ goto finish;
+ }
+
+ snprintf(modules, sizeof(modules), MODULE_DIRECTORY "/%s/modules.devname", kernel.release);
+ in = fopen(modules, "re");
+ if (in == NULL) {
+ if (errno == ENOENT) {
+ fprintf(stderr, "Warning: " MODULE_DIRECTORY "/%s/modules.devname not found - ignoring\n",
+ kernel.release);
+ ret = EXIT_SUCCESS;
+ } else {
+ fprintf(stderr, "Error: could not open " MODULE_DIRECTORY "/%s/modules.devname - %m\n",
+ kernel.release);
+ ret = EXIT_FAILURE;
+ }
+ goto finish;
+ }
+
+ r = mkdir_parents(output, 0755);
+ if (r < 0) {
+ fprintf(stderr, "Error: could not create parent directory for %s - %m.\n", output);
+ ret = EXIT_FAILURE;
+ goto finish;
+ }
+
+ out = fopen(output, "we");
+ if (out == NULL) {
+ fprintf(stderr, "Error: could not create %s - %m\n", output);
+ ret = EXIT_FAILURE;
+ goto finish;
+ }
+
+ while (fgets(buf, sizeof(buf), in) != NULL) {
+ char modname[PATH_MAX];
+ char devname[PATH_MAX];
+ char type;
+ unsigned int maj, min;
+ int matches;
+
+ if (buf[0] == '#')
+ continue;
+
+ matches = sscanf(buf, "%s %s %c%u:%u", modname, devname,
+ &type, &maj, &min);
+ if (matches != 5 || (type != 'c' && type != 'b')) {
+ fprintf(stderr, "Error: invalid devname entry: %s", buf);
+ ret = EXIT_FAILURE;
+ continue;
+ }
+
+ format->write(out, modname, devname, type, maj, min);
+ }
+
+finish:
+ if (in)
+ fclose(in);
+ if (out)
+ fclose(out);
+ return ret;
+}
+
+const struct kmod_cmd kmod_cmd_static_nodes = {
+ .name = "static-nodes",
+ .cmd = do_static_nodes,
+ .help = "outputs the static-node information installed with the currently running kernel",
+};