aboutsummaryrefslogtreecommitdiff
path: root/libexec
diff options
context:
space:
mode:
Diffstat (limited to 'libexec')
-rw-r--r--libexec/Makefile127
-rw-r--r--libexec/Makefile.inc3
-rw-r--r--libexec/atf/Makefile29
-rw-r--r--libexec/atf/Makefile.inc33
-rw-r--r--libexec/atf/atf-check/Makefile45
-rw-r--r--libexec/atf/atf-check/Makefile.depend20
-rw-r--r--libexec/atf/atf-check/Makefile.inc1
-rw-r--r--libexec/atf/atf-check/tests/Makefile8
-rw-r--r--libexec/atf/atf-check/tests/Makefile.depend10
-rw-r--r--libexec/atf/atf-pytest-wrapper/Makefile8
-rw-r--r--libexec/atf/atf-pytest-wrapper/Makefile.depend18
-rw-r--r--libexec/atf/atf-pytest-wrapper/atf_pytest_wrapper.cpp229
-rw-r--r--libexec/atf/atf-sh/Makefile80
-rw-r--r--libexec/atf/atf-sh/Makefile.depend20
-rw-r--r--libexec/atf/atf-sh/tests/Makefile26
-rw-r--r--libexec/atf/atf-sh/tests/Makefile.depend10
-rw-r--r--libexec/atf/tests/Makefile7
-rw-r--r--libexec/atf/tests/Makefile.depend10
-rw-r--r--libexec/atrun/LEGAL30
-rw-r--r--libexec/atrun/Makefile30
-rw-r--r--libexec/atrun/Makefile.depend17
-rw-r--r--libexec/atrun/atrun.c588
-rw-r--r--libexec/atrun/atrun.man83
-rw-r--r--libexec/atrun/gloadavg.c69
-rw-r--r--libexec/atrun/gloadavg.h28
-rw-r--r--libexec/blocklistd-helper/Makefile10
-rw-r--r--libexec/blocklistd-helper/Makefile.depend10
-rw-r--r--libexec/blocklistd-helper/blacklistd-helper293
-rw-r--r--libexec/bootpd/Announce64
-rw-r--r--libexec/bootpd/Changes293
-rwxr-xr-xlibexec/bootpd/ConvOldTab.sh141
-rw-r--r--libexec/bootpd/Installation29
-rw-r--r--libexec/bootpd/Makefile17
-rw-r--r--libexec/bootpd/Makefile.UNIX203
-rw-r--r--libexec/bootpd/Makefile.depend16
-rw-r--r--libexec/bootpd/Makefile.inc3
-rw-r--r--libexec/bootpd/Problems65
-rw-r--r--libexec/bootpd/README135
-rw-r--r--libexec/bootpd/ToDo61
-rw-r--r--libexec/bootpd/bootp.h147
-rw-r--r--libexec/bootpd/bootpd.8310
-rw-r--r--libexec/bootpd/bootpd.c1396
-rw-r--r--libexec/bootpd/bootpd.h211
-rw-r--r--libexec/bootpd/bootpgw/Makefile11
-rw-r--r--libexec/bootpd/bootpgw/Makefile.depend16
-rw-r--r--libexec/bootpd/bootpgw/bootpgw.c674
-rw-r--r--libexec/bootpd/bootptab.5428
-rw-r--r--libexec/bootpd/bootptab.cmu124
-rw-r--r--libexec/bootpd/bootptab.mcs90
-rw-r--r--libexec/bootpd/bptypes.h20
-rw-r--r--libexec/bootpd/dovend.c385
-rw-r--r--libexec/bootpd/dovend.h5
-rw-r--r--libexec/bootpd/dumptab.c361
-rw-r--r--libexec/bootpd/getether.c385
-rw-r--r--libexec/bootpd/getether.h3
-rw-r--r--libexec/bootpd/getif.c142
-rw-r--r--libexec/bootpd/getif.h3
-rw-r--r--libexec/bootpd/hash.c385
-rw-r--r--libexec/bootpd/hash.h147
-rw-r--r--libexec/bootpd/hwaddr.c333
-rw-r--r--libexec/bootpd/hwaddr.h34
-rw-r--r--libexec/bootpd/lookup.c117
-rw-r--r--libexec/bootpd/lookup.h7
-rw-r--r--libexec/bootpd/patchlevel.h6
-rw-r--r--libexec/bootpd/readfile.c2035
-rw-r--r--libexec/bootpd/readfile.h10
-rw-r--r--libexec/bootpd/report.c136
-rw-r--r--libexec/bootpd/report.h5
-rw-r--r--libexec/bootpd/rtmsg.c236
-rw-r--r--libexec/bootpd/syslog.conf63
-rw-r--r--libexec/bootpd/tools/Makefile5
-rw-r--r--libexec/bootpd/tools/Makefile.inc7
-rw-r--r--libexec/bootpd/tools/bootpef/Makefile12
-rw-r--r--libexec/bootpd/tools/bootpef/Makefile.depend16
-rw-r--r--libexec/bootpd/tools/bootpef/bootpef.863
-rw-r--r--libexec/bootpd/tools/bootpef/bootpef.c318
-rw-r--r--libexec/bootpd/tools/bootptest/Makefile11
-rw-r--r--libexec/bootpd/tools/bootptest/Makefile.depend16
-rw-r--r--libexec/bootpd/tools/bootptest/bootptest.876
-rw-r--r--libexec/bootpd/tools/bootptest/bootptest.c510
-rw-r--r--libexec/bootpd/tools/bootptest/bootptest.h17
-rw-r--r--libexec/bootpd/tools/bootptest/print-bootp.c476
-rw-r--r--libexec/bootpd/trygetea.c53
-rw-r--r--libexec/bootpd/trygetif.c72
-rw-r--r--libexec/bootpd/trylook.c56
-rw-r--r--libexec/bootpd/tzone.c46
-rw-r--r--libexec/bootpd/tzone.h3
-rw-r--r--libexec/comsat/Makefile4
-rw-r--r--libexec/comsat/Makefile.depend15
-rw-r--r--libexec/comsat/comsat.8108
-rw-r--r--libexec/comsat/comsat.c264
-rw-r--r--libexec/dma/Makefile4
-rw-r--r--libexec/dma/Makefile.inc12
-rw-r--r--libexec/dma/dma-mbox-create/Makefile8
-rw-r--r--libexec/dma/dma-mbox-create/Makefile.depend19
-rw-r--r--libexec/dma/dmagent/Makefile38
-rw-r--r--libexec/dma/dmagent/Makefile.depend19
-rw-r--r--libexec/dma/dmagent/auth.conf4
-rw-r--r--libexec/dma/dmagent/dma.conf63
-rw-r--r--libexec/dma/dmagent/mailer.conf9
-rw-r--r--libexec/fingerd/Makefile16
-rw-r--r--libexec/fingerd/Makefile.depend17
-rw-r--r--libexec/fingerd/Makefile.depend.options5
-rw-r--r--libexec/fingerd/fingerd.8158
-rw-r--r--libexec/fingerd/fingerd.c231
-rw-r--r--libexec/fingerd/pathnames.h32
-rw-r--r--libexec/flua/Makefile73
-rw-r--r--libexec/flua/Makefile.inc12
-rw-r--r--libexec/flua/bootstrap.h32
-rw-r--r--libexec/flua/flua.199
-rw-r--r--libexec/flua/lfbsd/Makefile5
-rw-r--r--libexec/flua/lfbsd/Makefile.inc2
-rw-r--r--libexec/flua/lfbsd/lfbsd.c289
-rw-r--r--libexec/flua/lfbsd/lfbsd.h32
-rw-r--r--libexec/flua/lfs/Makefile5
-rw-r--r--libexec/flua/lfs/Makefile.inc2
-rw-r--r--libexec/flua/lfs/lfs.c453
-rw-r--r--libexec/flua/lfs/lfs.h31
-rw-r--r--libexec/flua/libfreebsd/Makefile4
-rw-r--r--libexec/flua/libfreebsd/Makefile.inc3
-rw-r--r--libexec/flua/libfreebsd/kenv/Makefile5
-rw-r--r--libexec/flua/libfreebsd/kenv/Makefile.inc2
-rw-r--r--libexec/flua/libfreebsd/kenv/freebsd.kenv.3lua44
-rw-r--r--libexec/flua/libfreebsd/kenv/kenv.c100
-rw-r--r--libexec/flua/libfreebsd/sys/Makefile4
-rw-r--r--libexec/flua/libfreebsd/sys/Makefile.inc3
-rw-r--r--libexec/flua/libfreebsd/sys/linker/Makefile6
-rw-r--r--libexec/flua/libfreebsd/sys/linker/Makefile.inc2
-rw-r--r--libexec/flua/libfreebsd/sys/linker/freebsd.sys.linker.3lua46
-rw-r--r--libexec/flua/libfreebsd/sys/linker/linker.c86
-rw-r--r--libexec/flua/libhash/Makefile6
-rw-r--r--libexec/flua/libhash/Makefile.inc3
-rw-r--r--libexec/flua/libhash/hash.3lua54
-rw-r--r--libexec/flua/libhash/lhash.c183
-rw-r--r--libexec/flua/libhash/lhash.h11
-rw-r--r--libexec/flua/libjail/Makefile6
-rw-r--r--libexec/flua/libjail/Makefile.inc3
-rw-r--r--libexec/flua/libjail/jail.3lua277
-rw-r--r--libexec/flua/libjail/lua_jail.c722
-rw-r--r--libexec/flua/liblyaml/Makefile4
-rw-r--r--libexec/flua/liblyaml/Makefile.inc20
-rw-r--r--libexec/flua/libucl/Makefile4
-rw-r--r--libexec/flua/libucl/Makefile.inc12
-rw-r--r--libexec/flua/linit_flua.c95
-rw-r--r--libexec/flua/modules/lposix.c699
-rw-r--r--libexec/flua/modules/lposix.h10
-rw-r--r--libexec/getty/Makefile12
-rw-r--r--libexec/getty/Makefile.depend16
-rw-r--r--libexec/getty/chat.c485
-rw-r--r--libexec/getty/extern.h56
-rw-r--r--libexec/getty/getty.8122
-rw-r--r--libexec/getty/gettytab236
-rw-r--r--libexec/getty/gettytab.5515
-rw-r--r--libexec/getty/gettytab.h173
-rw-r--r--libexec/getty/init.c148
-rw-r--r--libexec/getty/main.c806
-rw-r--r--libexec/getty/pathnames.h35
-rw-r--r--libexec/getty/subr.c676
-rw-r--r--libexec/getty/ttys.5177
-rw-r--r--libexec/hyperv/Makefile9
-rw-r--r--libexec/hyperv/Makefile.depend10
-rw-r--r--libexec/mail.local/Makefile29
-rw-r--r--libexec/mail.local/Makefile.depend17
-rw-r--r--libexec/makewhatis.local/Makefile6
-rw-r--r--libexec/makewhatis.local/Makefile.depend10
-rw-r--r--libexec/makewhatis.local/makewhatis.local.867
-rw-r--r--libexec/makewhatis.local/makewhatis.local.sh55
-rw-r--r--libexec/mknetid/Makefile8
-rw-r--r--libexec/mknetid/Makefile.depend17
-rw-r--r--libexec/mknetid/hash.c165
-rw-r--r--libexec/mknetid/hash.h54
-rw-r--r--libexec/mknetid/mknetid.8150
-rw-r--r--libexec/mknetid/mknetid.c303
-rw-r--r--libexec/mknetid/netid.589
-rw-r--r--libexec/mknetid/parse_group.c151
-rw-r--r--libexec/nuageinit/Makefile12
-rw-r--r--libexec/nuageinit/Makefile.depend10
-rw-r--r--libexec/nuageinit/nuage.lua710
-rwxr-xr-xlibexec/nuageinit/nuageinit720
-rw-r--r--libexec/nuageinit/nuageinit.7431
-rw-r--r--libexec/nuageinit/tests/Makefile22
-rw-r--r--libexec/nuageinit/tests/Makefile.depend10
-rw-r--r--libexec/nuageinit/tests/addfile.lua71
-rw-r--r--libexec/nuageinit/tests/addgroup.lua16
-rw-r--r--libexec/nuageinit/tests/addsshkey.lua5
-rw-r--r--libexec/nuageinit/tests/adduser.lua16
-rw-r--r--libexec/nuageinit/tests/adduser_passwd.lua20
-rw-r--r--libexec/nuageinit/tests/dirname.lua11
-rw-r--r--libexec/nuageinit/tests/err.lua5
-rw-r--r--libexec/nuageinit/tests/nuage.sh101
-rw-r--r--libexec/nuageinit/tests/nuageinit.sh947
-rw-r--r--libexec/nuageinit/tests/sethostname.lua5
-rw-r--r--libexec/nuageinit/tests/settimezone.lua5
-rw-r--r--libexec/nuageinit/tests/utils.sh32
-rw-r--r--libexec/nuageinit/tests/warn.lua5
-rw-r--r--libexec/phttpget/Makefile4
-rw-r--r--libexec/phttpget/Makefile.depend16
-rw-r--r--libexec/phttpget/phttpget.882
-rw-r--r--libexec/phttpget/phttpget.c729
-rw-r--r--libexec/pppoed/Makefile9
-rw-r--r--libexec/pppoed/Makefile.depend17
-rw-r--r--libexec/pppoed/pppoed.8217
-rw-r--r--libexec/pppoed/pppoed.c692
-rw-r--r--libexec/rbootd/Makefile8
-rw-r--r--libexec/rbootd/Makefile.depend15
-rw-r--r--libexec/rbootd/bpf.c398
-rw-r--r--libexec/rbootd/conf.c81
-rw-r--r--libexec/rbootd/defs.h180
-rw-r--r--libexec/rbootd/parseconf.c350
-rw-r--r--libexec/rbootd/pathnames.h47
-rw-r--r--libexec/rbootd/rbootd.8150
-rw-r--r--libexec/rbootd/rbootd.c435
-rw-r--r--libexec/rbootd/rmp.h91
-rw-r--r--libexec/rbootd/rmp_var.h240
-rw-r--r--libexec/rbootd/rmpproto.c576
-rw-r--r--libexec/rbootd/utils.c536
-rw-r--r--libexec/rc/Makefile31
-rwxr-xr-xlibexec/rc/debug.sh451
-rwxr-xr-xlibexec/rc/hooks.sh274
-rwxr-xr-xlibexec/rc/netstart52
-rw-r--r--libexec/rc/network.subr1790
-rwxr-xr-xlibexec/rc/pccard_ether147
-rw-r--r--libexec/rc/rc152
-rw-r--r--libexec/rc/rc.bsdextended137
-rw-r--r--libexec/rc/rc.conf794
-rwxr-xr-xlibexec/rc/rc.d/DAEMON9
-rwxr-xr-xlibexec/rc/rc.d/FILESYSTEMS11
-rwxr-xr-xlibexec/rc/rc.d/LOGIN12
-rw-r--r--libexec/rc/rc.d/Makefile384
-rwxr-xr-xlibexec/rc/rc.d/NETWORKING11
-rwxr-xr-xlibexec/rc/rc.d/SERVERS9
-rwxr-xr-xlibexec/rc/rc.d/accounting83
-rwxr-xr-xlibexec/rc/rc.d/adjkerntz21
-rwxr-xr-xlibexec/rc/rc.d/apm50
-rwxr-xr-xlibexec/rc/rc.d/apmd41
-rwxr-xr-xlibexec/rc/rc.d/auditd39
-rwxr-xr-xlibexec/rc/rc.d/auditdistd23
-rwxr-xr-xlibexec/rc/rc.d/automount35
-rwxr-xr-xlibexec/rc/rc.d/automountd24
-rwxr-xr-xlibexec/rc/rc.d/autounmountd23
-rwxr-xr-xlibexec/rc/rc.d/bgfsck53
-rwxr-xr-xlibexec/rc/rc.d/blacklistd54
-rwxr-xr-xlibexec/rc/rc.d/blocklistd46
-rwxr-xr-xlibexec/rc/rc.d/bluetooth333
-rwxr-xr-xlibexec/rc/rc.d/bootparams21
-rwxr-xr-xlibexec/rc/rc.d/bridge97
-rwxr-xr-xlibexec/rc/rc.d/bsnmpd21
-rwxr-xr-xlibexec/rc/rc.d/bthidd56
-rwxr-xr-xlibexec/rc/rc.d/ccd28
-rwxr-xr-xlibexec/rc/rc.d/cfumass152
-rwxr-xr-xlibexec/rc/rc.d/cleanvar50
-rwxr-xr-xlibexec/rc/rc.d/cleartmp64
-rwxr-xr-xlibexec/rc/rc.d/cron28
-rwxr-xr-xlibexec/rc/rc.d/ctld26
-rwxr-xr-xlibexec/rc/rc.d/ddb41
-rwxr-xr-xlibexec/rc/rc.d/defaultroute77
-rwxr-xr-xlibexec/rc/rc.d/devd44
-rwxr-xr-xlibexec/rc/rc.d/devfs75
-rwxr-xr-xlibexec/rc/rc.d/devmatch88
-rwxr-xr-xlibexec/rc/rc.d/dhclient76
-rwxr-xr-xlibexec/rc/rc.d/dmesg30
-rwxr-xr-xlibexec/rc/rc.d/dnctl29
-rwxr-xr-xlibexec/rc/rc.d/dumpon104
-rwxr-xr-xlibexec/rc/rc.d/fsck98
-rwxr-xr-xlibexec/rc/rc.d/ftp-proxy77
-rwxr-xr-xlibexec/rc/rc.d/geli128
-rwxr-xr-xlibexec/rc/rc.d/geli262
-rwxr-xr-xlibexec/rc/rc.d/ggated22
-rwxr-xr-xlibexec/rc/rc.d/gptboot80
-rwxr-xr-xlibexec/rc/rc.d/growfs313
-rwxr-xr-xlibexec/rc/rc.d/growfs_fstab65
-rwxr-xr-xlibexec/rc/rc.d/gssd19
-rwxr-xr-xlibexec/rc/rc.d/hastd33
-rwxr-xr-xlibexec/rc/rc.d/hcsecd27
-rwxr-xr-xlibexec/rc/rc.d/hostapd36
-rwxr-xr-xlibexec/rc/rc.d/hostid165
-rwxr-xr-xlibexec/rc/rc.d/hostid_save51
-rwxr-xr-xlibexec/rc/rc.d/hostname84
-rwxr-xr-xlibexec/rc/rc.d/inetd22
-rwxr-xr-xlibexec/rc/rc.d/iovctl42
-rwxr-xr-xlibexec/rc/rc.d/ip6addrctl127
-rwxr-xr-xlibexec/rc/rc.d/ipfilter88
-rwxr-xr-xlibexec/rc/rc.d/ipfs56
-rwxr-xr-xlibexec/rc/rc.d/ipfw169
-rwxr-xr-xlibexec/rc/rc.d/ipfw_netflow79
-rwxr-xr-xlibexec/rc/rc.d/ipmon36
-rwxr-xr-xlibexec/rc/rc.d/ipnat30
-rwxr-xr-xlibexec/rc/rc.d/ippool40
-rwxr-xr-xlibexec/rc/rc.d/ipropd_master43
-rwxr-xr-xlibexec/rc/rc.d/ipropd_slave35
-rwxr-xr-xlibexec/rc/rc.d/ipsec64
-rwxr-xr-xlibexec/rc/rc.d/iscsictl24
-rwxr-xr-xlibexec/rc/rc.d/iscsid24
-rwxr-xr-xlibexec/rc/rc.d/jail616
-rwxr-xr-xlibexec/rc/rc.d/kadmind24
-rwxr-xr-xlibexec/rc/rc.d/kdc67
-rwxr-xr-xlibexec/rc/rc.d/kfd19
-rwxr-xr-xlibexec/rc/rc.d/kld58
-rwxr-xr-xlibexec/rc/rc.d/kldxref40
-rwxr-xr-xlibexec/rc/rc.d/kpasswdd24
-rwxr-xr-xlibexec/rc/rc.d/ldconfig79
-rwxr-xr-xlibexec/rc/rc.d/linux88
-rwxr-xr-xlibexec/rc/rc.d/local40
-rwxr-xr-xlibexec/rc/rc.d/local_unbound123
-rwxr-xr-xlibexec/rc/rc.d/localpkg83
-rwxr-xr-xlibexec/rc/rc.d/lockd34
-rwxr-xr-xlibexec/rc/rc.d/lpd29
-rwxr-xr-xlibexec/rc/rc.d/mdconfig199
-rwxr-xr-xlibexec/rc/rc.d/mdconfig2229
-rwxr-xr-xlibexec/rc/rc.d/mixer107
-rwxr-xr-xlibexec/rc/rc.d/motd62
-rwxr-xr-xlibexec/rc/rc.d/mountcritlocal76
-rwxr-xr-xlibexec/rc/rc.d/mountcritremote93
-rwxr-xr-xlibexec/rc/rc.d/mountd79
-rwxr-xr-xlibexec/rc/rc.d/mountlate51
-rwxr-xr-xlibexec/rc/rc.d/moused82
-rwxr-xr-xlibexec/rc/rc.d/msconvd61
-rwxr-xr-xlibexec/rc/rc.d/msgs29
-rwxr-xr-xlibexec/rc/rc.d/natd47
-rwxr-xr-xlibexec/rc/rc.d/netif275
-rwxr-xr-xlibexec/rc/rc.d/netoptions129
-rwxr-xr-xlibexec/rc/rc.d/netwait156
-rwxr-xr-xlibexec/rc/rc.d/newsyslog30
-rwxr-xr-xlibexec/rc/rc.d/nfscbd21
-rwxr-xr-xlibexec/rc/rc.d/nfsclient53
-rwxr-xr-xlibexec/rc/rc.d/nfsd68
-rwxr-xr-xlibexec/rc/rc.d/nfsuserd32
-rwxr-xr-xlibexec/rc/rc.d/nisdomain58
-rwxr-xr-xlibexec/rc/rc.d/noshutdown31
-rwxr-xr-xlibexec/rc/rc.d/nscd56
-rwxr-xr-xlibexec/rc/rc.d/ntpd250
-rwxr-xr-xlibexec/rc/rc.d/ntpdate38
-rwxr-xr-xlibexec/rc/rc.d/nuageinit96
-rwxr-xr-xlibexec/rc/rc.d/nuageinit_post_net25
-rwxr-xr-xlibexec/rc/rc.d/nuageinit_user_data_script29
-rwxr-xr-xlibexec/rc/rc.d/opensm29
-rwxr-xr-xlibexec/rc/rc.d/os-release48
-rwxr-xr-xlibexec/rc/rc.d/pf94
-rwxr-xr-xlibexec/rc/rc.d/pflog111
-rwxr-xr-xlibexec/rc/rc.d/pfsync52
-rwxr-xr-xlibexec/rc/rc.d/power_profile99
-rwxr-xr-xlibexec/rc/rc.d/powerd22
-rwxr-xr-xlibexec/rc/rc.d/ppp138
-rwxr-xr-xlibexec/rc/rc.d/pppoed37
-rwxr-xr-xlibexec/rc/rc.d/pwcheck31
-rwxr-xr-xlibexec/rc/rc.d/quota37
-rwxr-xr-xlibexec/rc/rc.d/random158
-rwxr-xr-xlibexec/rc/rc.d/rarpd23
-rwxr-xr-xlibexec/rc/rc.d/rctl45
-rwxr-xr-xlibexec/rc/rc.d/resolv66
-rwxr-xr-xlibexec/rc/rc.d/rfcomm_pppd_server126
-rwxr-xr-xlibexec/rc/rc.d/root46
-rwxr-xr-xlibexec/rc/rc.d/route6d22
-rwxr-xr-xlibexec/rc/rc.d/routed23
-rwxr-xr-xlibexec/rc/rc.d/routing442
-rwxr-xr-xlibexec/rc/rc.d/rpcbind21
-rwxr-xr-xlibexec/rc/rc.d/rtadvd77
-rwxr-xr-xlibexec/rc/rc.d/rtsold28
-rwxr-xr-xlibexec/rc/rc.d/rwho20
-rwxr-xr-xlibexec/rc/rc.d/savecore85
-rwxr-xr-xlibexec/rc/rc.d/sdpd27
-rwxr-xr-xlibexec/rc/rc.d/securelevel29
-rwxr-xr-xlibexec/rc/rc.d/sendmail229
-rwxr-xr-xlibexec/rc/rc.d/serial158
-rwxr-xr-xlibexec/rc/rc.d/sshd87
-rwxr-xr-xlibexec/rc/rc.d/statd33
-rwxr-xr-xlibexec/rc/rc.d/static_arp77
-rwxr-xr-xlibexec/rc/rc.d/static_ndp76
-rwxr-xr-xlibexec/rc/rc.d/stf82
-rwxr-xr-xlibexec/rc/rc.d/swap21
-rwxr-xr-xlibexec/rc/rc.d/swaplate21
-rwxr-xr-xlibexec/rc/rc.d/syscons406
-rwxr-xr-xlibexec/rc/rc.d/sysctl41
-rwxr-xr-xlibexec/rc/rc.d/sysctl_lastload21
-rwxr-xr-xlibexec/rc/rc.d/syslogd75
-rwxr-xr-xlibexec/rc/rc.d/sysvipc29
-rwxr-xr-xlibexec/rc/rc.d/tlsclntd22
-rwxr-xr-xlibexec/rc/rc.d/tlsservd26
-rwxr-xr-xlibexec/rc/rc.d/tmp81
-rwxr-xr-xlibexec/rc/rc.d/ubthidhci43
-rwxr-xr-xlibexec/rc/rc.d/ugidfw55
-rwxr-xr-xlibexec/rc/rc.d/utx23
-rwxr-xr-xlibexec/rc/rc.d/var114
-rwxr-xr-xlibexec/rc/rc.d/var_run50
-rwxr-xr-xlibexec/rc/rc.d/virecover69
-rw-r--r--libexec/rc/rc.d/virtual_oss119
-rwxr-xr-xlibexec/rc/rc.d/watchdogd95
-rwxr-xr-xlibexec/rc/rc.d/wpa_supplicant39
-rwxr-xr-xlibexec/rc/rc.d/ypbind38
-rwxr-xr-xlibexec/rc/rc.d/ypldap28
-rwxr-xr-xlibexec/rc/rc.d/yppasswdd39
-rwxr-xr-xlibexec/rc/rc.d/ypserv41
-rwxr-xr-xlibexec/rc/rc.d/ypset39
-rwxr-xr-xlibexec/rc/rc.d/ypupdated35
-rwxr-xr-xlibexec/rc/rc.d/ypxfrd38
-rwxr-xr-xlibexec/rc/rc.d/zfs82
-rwxr-xr-xlibexec/rc/rc.d/zfsbe92
-rwxr-xr-xlibexec/rc/rc.d/zfsd20
-rwxr-xr-xlibexec/rc/rc.d/zfskeys131
-rwxr-xr-xlibexec/rc/rc.d/zpool40
-rwxr-xr-xlibexec/rc/rc.d/zpoolreguid29
-rwxr-xr-xlibexec/rc/rc.d/zpoolupgrade29
-rwxr-xr-xlibexec/rc/rc.d/zvol49
-rw-r--r--libexec/rc/rc.firewall553
-rw-r--r--libexec/rc/rc.initdiskless408
-rwxr-xr-xlibexec/rc/rc.resume70
-rw-r--r--libexec/rc/rc.shutdown115
-rw-r--r--libexec/rc/rc.subr2880
-rwxr-xr-xlibexec/rc/rc.suspend79
-rw-r--r--libexec/rc/safe_eval.sh100
-rw-r--r--libexec/rc/tests/Makefile3
-rw-r--r--libexec/rc/tests/rc_subr_test.sh148
-rw-r--r--libexec/revnetgroup/Makefile8
-rw-r--r--libexec/revnetgroup/Makefile.depend15
-rw-r--r--libexec/revnetgroup/hash.c205
-rw-r--r--libexec/revnetgroup/hash.h67
-rw-r--r--libexec/revnetgroup/parse_netgroup.c357
-rw-r--r--libexec/revnetgroup/revnetgroup.8157
-rw-r--r--libexec/revnetgroup/revnetgroup.c176
-rw-r--r--libexec/rpc.rquotad/Makefile9
-rw-r--r--libexec/rpc.rquotad/Makefile.depend20
-rw-r--r--libexec/rpc.rquotad/rpc.rquotad.865
-rw-r--r--libexec/rpc.rquotad/rquotad.c314
-rw-r--r--libexec/rpc.rstatd/Makefile10
-rw-r--r--libexec/rpc.rstatd/Makefile.depend20
-rw-r--r--libexec/rpc.rstatd/rpc.rstatd.859
-rw-r--r--libexec/rpc.rstatd/rstat_proc.c469
-rw-r--r--libexec/rpc.rstatd/rstatd.c126
-rw-r--r--libexec/rpc.rusersd/Makefile13
-rw-r--r--libexec/rpc.rusersd/Makefile.depend18
-rw-r--r--libexec/rpc.rusersd/extern.h34
-rw-r--r--libexec/rpc.rusersd/rpc.rusersd.862
-rw-r--r--libexec/rpc.rusersd/rusers_proc.c329
-rw-r--r--libexec/rpc.rusersd/rusersd.c109
-rw-r--r--libexec/rpc.rwalld/Makefile10
-rw-r--r--libexec/rpc.rwalld/Makefile.depend19
-rw-r--r--libexec/rpc.rwalld/rpc.rwalld.877
-rw-r--r--libexec/rpc.rwalld/rwalld.c205
-rw-r--r--libexec/rpc.sprayd/Makefile7
-rw-r--r--libexec/rpc.sprayd/Makefile.depend18
-rw-r--r--libexec/rpc.sprayd/rpc.sprayd.854
-rw-r--r--libexec/rpc.sprayd/sprayd.c160
-rw-r--r--libexec/rtld-elf/Makefile134
-rw-r--r--libexec/rtld-elf/Makefile.depend15
-rw-r--r--libexec/rtld-elf/Symbol.map43
-rw-r--r--libexec/rtld-elf/aarch64/reloc.c626
-rw-r--r--libexec/rtld-elf/aarch64/rtld_machdep.h97
-rw-r--r--libexec/rtld-elf/aarch64/rtld_start.S254
-rw-r--r--libexec/rtld-elf/amd64/Makefile.inc1
-rw-r--r--libexec/rtld-elf/amd64/reloc.c582
-rw-r--r--libexec/rtld-elf/amd64/rtld_machdep.h83
-rw-r--r--libexec/rtld-elf/amd64/rtld_start.S174
-rw-r--r--libexec/rtld-elf/arm/Makefile.inc1
-rw-r--r--libexec/rtld-elf/arm/reloc.c469
-rw-r--r--libexec/rtld-elf/arm/rtld_machdep.h84
-rw-r--r--libexec/rtld-elf/arm/rtld_start.S98
-rw-r--r--libexec/rtld-elf/debug.c144
-rw-r--r--libexec/rtld-elf/debug.h57
-rw-r--r--libexec/rtld-elf/i386/Makefile.inc1
-rw-r--r--libexec/rtld-elf/i386/Symbol.map6
-rw-r--r--libexec/rtld-elf/i386/reloc.c555
-rw-r--r--libexec/rtld-elf/i386/rtld_machdep.h84
-rw-r--r--libexec/rtld-elf/i386/rtld_start.S98
-rw-r--r--libexec/rtld-elf/libmap.c497
-rw-r--r--libexec/rtld-elf/libmap.conf1
-rw-r--r--libexec/rtld-elf/libmap.h12
-rw-r--r--libexec/rtld-elf/map_object.c525
-rw-r--r--libexec/rtld-elf/powerpc/Makefile.inc0
-rw-r--r--libexec/rtld-elf/powerpc/reloc.c842
-rw-r--r--libexec/rtld-elf/powerpc/rtld_machdep.h101
-rw-r--r--libexec/rtld-elf/powerpc/rtld_start.S319
-rw-r--r--libexec/rtld-elf/powerpc64/Makefile.inc1
-rw-r--r--libexec/rtld-elf/powerpc64/reloc.c739
-rw-r--r--libexec/rtld-elf/powerpc64/rtld_machdep.h93
-rw-r--r--libexec/rtld-elf/powerpc64/rtld_start.S179
-rw-r--r--libexec/rtld-elf/riscv/reloc.c476
-rw-r--r--libexec/rtld-elf/riscv/rtld_machdep.h113
-rw-r--r--libexec/rtld-elf/riscv/rtld_start.S129
-rw-r--r--libexec/rtld-elf/rtld-libc/Makefile.inc110
-rw-r--r--libexec/rtld-elf/rtld-libc/libc_private.h37
-rw-r--r--libexec/rtld-elf/rtld-libc/namespace.h37
-rw-r--r--libexec/rtld-elf/rtld-libc/rtld_libc.c120
-rw-r--r--libexec/rtld-elf/rtld-libc/rtld_libc.h86
-rw-r--r--libexec/rtld-elf/rtld-libc/un-namespace.h39
-rw-r--r--libexec/rtld-elf/rtld.1543
-rw-r--r--libexec/rtld-elf/rtld.c6737
-rw-r--r--libexec/rtld-elf/rtld.h443
-rw-r--r--libexec/rtld-elf/rtld_lock.c505
-rw-r--r--libexec/rtld-elf/rtld_lock.h102
-rw-r--r--libexec/rtld-elf/rtld_malloc.c322
-rw-r--r--libexec/rtld-elf/rtld_malloc.h43
-rw-r--r--libexec/rtld-elf/rtld_paths.h96
-rw-r--r--libexec/rtld-elf/rtld_printf.c514
-rw-r--r--libexec/rtld-elf/rtld_printf.h48
-rw-r--r--libexec/rtld-elf/rtld_tls.h69
-rw-r--r--libexec/rtld-elf/rtld_utrace.h61
-rw-r--r--libexec/rtld-elf/tests/Makefile19
-rw-r--r--libexec/rtld-elf/tests/Makefile.depend17
-rw-r--r--libexec/rtld-elf/tests/Makefile.inc3
-rw-r--r--libexec/rtld-elf/tests/common.c79
-rw-r--r--libexec/rtld-elf/tests/common.h41
-rw-r--r--libexec/rtld-elf/tests/dlopen_test.c52
-rw-r--r--libexec/rtld-elf/tests/ld_library_pathfds.c169
-rw-r--r--libexec/rtld-elf/tests/ld_preload_fds.c106
-rw-r--r--libexec/rtld-elf/tests/libdeep/Makefile14
-rw-r--r--libexec/rtld-elf/tests/libdeep/libdeep.c28
-rw-r--r--libexec/rtld-elf/tests/libpythagoras/Makefile13
-rw-r--r--libexec/rtld-elf/tests/libpythagoras/Makefile.depend16
-rw-r--r--libexec/rtld-elf/tests/libpythagoras/pythagoras.c40
-rw-r--r--libexec/rtld-elf/tests/libpythagoras/pythagoras.h26
-rw-r--r--libexec/rtld-elf/tests/libval/Makefile9
-rw-r--r--libexec/rtld-elf/tests/libval/libval.c26
-rw-r--r--libexec/rtld-elf/tests/libval2/Makefile7
-rw-r--r--libexec/rtld-elf/tests/rtld_deepbind/Makefile9
-rw-r--r--libexec/rtld-elf/tests/rtld_deepbind/rtld_deepbind.c65
-rw-r--r--libexec/rtld-elf/tests/target/Makefile15
-rw-r--r--libexec/rtld-elf/tests/target/Makefile.depend16
-rw-r--r--libexec/rtld-elf/tests/target/target.c37
-rw-r--r--libexec/rtld-elf/xmalloc.c92
-rw-r--r--libexec/rtld-elf32/Makefile9
-rw-r--r--libexec/save-entropy/Makefile4
-rw-r--r--libexec/save-entropy/Makefile.depend10
-rw-r--r--libexec/save-entropy/save-entropy.897
-rwxr-xr-xlibexec/save-entropy/save-entropy.sh132
-rw-r--r--libexec/smrsh/Makefile28
-rw-r--r--libexec/smrsh/Makefile.depend16
-rw-r--r--libexec/talkd/Makefile7
-rw-r--r--libexec/talkd/Makefile.depend17
-rw-r--r--libexec/talkd/announce.c159
-rw-r--r--libexec/talkd/extern.h43
-rw-r--r--libexec/talkd/print.c83
-rw-r--r--libexec/talkd/process.c215
-rw-r--r--libexec/talkd/table.c227
-rw-r--r--libexec/talkd/talkd.873
-rw-r--r--libexec/talkd/talkd.c125
-rw-r--r--libexec/tcpd/Makefile21
-rw-r--r--libexec/tcpd/Makefile.depend16
-rw-r--r--libexec/tests/Makefile4
-rw-r--r--libexec/tests/Makefile.depend10
-rw-r--r--libexec/tftp-proxy/Makefile13
-rw-r--r--libexec/tftp-proxy/Makefile.depend18
-rw-r--r--libexec/tftpd/Makefile18
-rw-r--r--libexec/tftpd/Makefile.depend16
-rw-r--r--libexec/tftpd/Makefile.depend.options5
-rw-r--r--libexec/tftpd/tests/Makefile8
-rw-r--r--libexec/tftpd/tests/functional.c1283
-rw-r--r--libexec/tftpd/tftp-file.c299
-rw-r--r--libexec/tftpd/tftp-file.h39
-rw-r--r--libexec/tftpd/tftp-io.c446
-rw-r--r--libexec/tftpd/tftp-io.h46
-rw-r--r--libexec/tftpd/tftp-options.c477
-rw-r--r--libexec/tftpd/tftp-options.h68
-rw-r--r--libexec/tftpd/tftp-transfer.c446
-rw-r--r--libexec/tftpd/tftp-transfer.h30
-rw-r--r--libexec/tftpd/tftp-utils.c328
-rw-r--r--libexec/tftpd/tftp-utils.h129
-rw-r--r--libexec/tftpd/tftpd.8333
-rw-r--r--libexec/tftpd/tftpd.c849
-rw-r--r--libexec/ulog-helper/Makefile8
-rw-r--r--libexec/ulog-helper/Makefile.depend16
-rw-r--r--libexec/ulog-helper/ulog-helper.c97
-rw-r--r--libexec/ypxfr/Makefile45
-rw-r--r--libexec/ypxfr/Makefile.depend19
-rw-r--r--libexec/ypxfr/yp_dbwrite.c110
-rw-r--r--libexec/ypxfr/ypxfr.8306
-rw-r--r--libexec/ypxfr/ypxfr_extern.h62
-rw-r--r--libexec/ypxfr/ypxfr_getmap.c99
-rw-r--r--libexec/ypxfr/ypxfr_main.c577
-rw-r--r--libexec/ypxfr/ypxfr_misc.c293
-rw-r--r--libexec/ypxfr/ypxfrd_getmap.c144
570 files changed, 80744 insertions, 0 deletions
diff --git a/libexec/Makefile b/libexec/Makefile
new file mode 100644
index 000000000000..180dd10b5d29
--- /dev/null
+++ b/libexec/Makefile
@@ -0,0 +1,127 @@
+.include <src.opts.mk>
+
+.include <bsd.compat.pre.mk>
+
+SUBDIR= ${_atf} \
+ ${_atrun} \
+ ${_blocklistd-helper} \
+ ${_comsat} \
+ ${_dma} \
+ flua \
+ getty \
+ ${_hyperv} \
+ ${_mail.local} \
+ ${_makewhatis.local} \
+ ${_mknetid} \
+ ${_phttpget} \
+ ${_pppoed} \
+ rc \
+ revnetgroup \
+ ${_rlogind} \
+ rpc.rquotad \
+ rpc.rstatd \
+ rpc.rusersd \
+ rpc.rwalld \
+ rpc.sprayd \
+ ${_rshd} \
+ ${_rtld-elf} \
+ save-entropy \
+ ${_nuageinit} \
+ ${_smrsh} \
+ ${_tests} \
+ ${_tftp-proxy} \
+ ulog-helper \
+ ${_ypxfr}
+
+.if ${MK_AT} != "no"
+_atrun= atrun
+.endif
+
+.if ${MK_BLOCKLIST} != "no"
+_blocklistd-helper+= blocklistd-helper
+.endif
+
+.if ${MK_BOOTPD} != "no"
+SUBDIR+= bootpd
+.endif
+
+.if ${MK_FINGER} != "no"
+SUBDIR+= fingerd
+.endif
+
+.if ${MK_FREEBSD_UPDATE} != "no"
+_phttpget= phttpget
+.endif
+
+.if ${MK_MAIL} != "no"
+_comsat= comsat
+.endif
+
+.if ${MK_DMAGENT} != "no"
+_dma= dma
+.endif
+
+.if ${MK_HYPERV} != "no"
+_hyperv+= hyperv
+.endif
+
+.if ${MK_NIS} != "no"
+_mknetid= mknetid
+_ypxfr= ypxfr
+.endif
+
+.if ${MK_NETGRAPH} != "no"
+_pppoed= pppoed
+.endif
+
+.if ${MK_PF} != "no"
+_tftp-proxy= tftp-proxy
+.endif
+
+.if !defined(NO_PIC) && !defined(NO_RTLD)
+_rtld-elf= rtld-elf
+.for LIBCOMPAT libcompat in ${_ALL_LIBCOMPATS_libcompats}
+SUBDIR.${MK_LIB${LIBCOMPAT}}+= rtld-elf${libcompat}
+.endfor
+.endif
+
+.if ${MK_RBOOTD} != "no"
+SUBDIR+= rbootd
+.endif
+
+.if ${MK_SENDMAIL} != "no"
+_mail.local= mail.local
+_smrsh= smrsh
+.endif
+
+.if ${MK_MAN_UTILS} != "no"
+_makewhatis.local= makewhatis.local
+.endif
+
+.if ${MK_TALK} != "no"
+SUBDIR+= talkd
+.endif
+
+.if ${MK_TCP_WRAPPERS} != "no"
+SUBDIR+= tcpd
+.endif
+
+.if ${MK_TFTP} != "no"
+SUBDIR+= tftpd
+.endif
+
+.if ${MK_TESTS} != "no"
+_tests= tests
+.endif
+
+.if ${MK_TESTS_SUPPORT} != "no"
+_atf= atf
+.endif
+
+.if ${MK_NUAGEINIT} != "no"
+_nuageinit= nuageinit
+.endif
+
+.include <bsd.arch.inc.mk>
+
+.include <bsd.subdir.mk>
diff --git a/libexec/Makefile.inc b/libexec/Makefile.inc
new file mode 100644
index 000000000000..8cf924798785
--- /dev/null
+++ b/libexec/Makefile.inc
@@ -0,0 +1,3 @@
+BINDIR?= /usr/libexec
+
+WFORMAT?= 1
diff --git a/libexec/atf/Makefile b/libexec/atf/Makefile
new file mode 100644
index 000000000000..e3002d6c7626
--- /dev/null
+++ b/libexec/atf/Makefile
@@ -0,0 +1,29 @@
+#-
+# Copyright (c) 2011 Google, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+SUBDIR= atf-check atf-pytest-wrapper atf-sh tests
+
+.include <bsd.subdir.mk>
diff --git a/libexec/atf/Makefile.inc b/libexec/atf/Makefile.inc
new file mode 100644
index 000000000000..5fd06c35cd09
--- /dev/null
+++ b/libexec/atf/Makefile.inc
@@ -0,0 +1,33 @@
+#-
+# Copyright (c) 2011 Google, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+PACKAGE?= atf
+LIB_PACKAGE=
+CFLAGS+= -DHAVE_CONFIG_H
+
+WARNS?= 3
+
+.include "../Makefile.inc"
diff --git a/libexec/atf/atf-check/Makefile b/libexec/atf/atf-check/Makefile
new file mode 100644
index 000000000000..cf598e384c86
--- /dev/null
+++ b/libexec/atf/atf-check/Makefile
@@ -0,0 +1,45 @@
+#-
+# Copyright (c) 2011 Google, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+.include <src.opts.mk>
+.include <bsd.init.mk>
+
+ATF= ${SRCTOP}/contrib/atf
+.PATH: ${ATF}/atf-sh
+
+PROG_CXX= atf-check
+SRCS= atf-check.cpp
+MAN= atf-check.1
+
+CFLAGS+= -I${ATF}
+CFLAGS+= -DATF_SHELL='"/bin/sh"'
+
+LIBADD= atf_cxx
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
+.include <bsd.prog.mk>
diff --git a/libexec/atf/atf-check/Makefile.depend b/libexec/atf/atf-check/Makefile.depend
new file mode 100644
index 000000000000..7886e7624456
--- /dev/null
+++ b/libexec/atf/atf-check/Makefile.depend
@@ -0,0 +1,20 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/atf/libatf-c++ \
+ lib/libc \
+ lib/libc++ \
+ lib/libcompiler_rt \
+ lib/libcxxrt \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/atf/atf-check/Makefile.inc b/libexec/atf/atf-check/Makefile.inc
new file mode 100644
index 000000000000..01b5f23410c8
--- /dev/null
+++ b/libexec/atf/atf-check/Makefile.inc
@@ -0,0 +1 @@
+.include "../Makefile.inc"
diff --git a/libexec/atf/atf-check/tests/Makefile b/libexec/atf/atf-check/tests/Makefile
new file mode 100644
index 000000000000..6e21e4ede211
--- /dev/null
+++ b/libexec/atf/atf-check/tests/Makefile
@@ -0,0 +1,8 @@
+ATF= ${SRCTOP}/contrib/atf
+.PATH: ${ATF}/atf-sh
+
+PACKAGE= tests
+
+ATF_TESTS_SH= atf-check_test
+
+.include <bsd.test.mk>
diff --git a/libexec/atf/atf-check/tests/Makefile.depend b/libexec/atf/atf-check/tests/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/atf/atf-check/tests/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/atf/atf-pytest-wrapper/Makefile b/libexec/atf/atf-pytest-wrapper/Makefile
new file mode 100644
index 000000000000..75b1bc3e1004
--- /dev/null
+++ b/libexec/atf/atf-pytest-wrapper/Makefile
@@ -0,0 +1,8 @@
+.include <src.opts.mk>
+.include <bsd.init.mk>
+
+PROG_CXX= atf_pytest_wrapper
+SRCS= atf_pytest_wrapper.cpp
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/libexec/atf/atf-pytest-wrapper/Makefile.depend b/libexec/atf/atf-pytest-wrapper/Makefile.depend
new file mode 100644
index 000000000000..76a8d2cdc8ca
--- /dev/null
+++ b/libexec/atf/atf-pytest-wrapper/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libc++ \
+ lib/libcompiler_rt \
+ lib/libcxxrt \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/atf/atf-pytest-wrapper/atf_pytest_wrapper.cpp b/libexec/atf/atf-pytest-wrapper/atf_pytest_wrapper.cpp
new file mode 100644
index 000000000000..b0cc600bde21
--- /dev/null
+++ b/libexec/atf/atf-pytest-wrapper/atf_pytest_wrapper.cpp
@@ -0,0 +1,229 @@
+// vim: ts=2 sw=2 et
+
+#include <format>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+#include <stdlib.h>
+#include <unistd.h>
+
+class Handler {
+ private:
+ const std::string kPytestName = "pytest";
+ const std::string kCleanupSuffix = ":cleanup";
+ const std::string kPythonPathEnv = "PYTHONPATH";
+ const std::string kAtfVar = "_ATF_VAR_";
+ public:
+ // Test listing requested
+ bool flag_list = false;
+ // Output debug data (will break listing)
+ bool flag_debug = false;
+ // Cleanup for the test requested
+ bool flag_cleanup = false;
+ // Test source directory (provided by ATF)
+ std::string src_dir;
+ // Path to write test status to (provided by ATF)
+ std::string dst_file;
+ // Path to add to PYTHONPATH (provided by the schebang args)
+ std::string python_path;
+ // Path to the script (provided by the schebang wrapper)
+ std::string script_path;
+ // Name of the test to run (provided by ATF)
+ std::string test_name;
+ // kv pairs (provided by ATF)
+ std::map<std::string,std::string> kv_map;
+ // our binary name
+ std::string binary_name;
+
+ static std::vector<std::string> ToVector(int argc, char **argv) {
+ std::vector<std::string> ret;
+
+ for (int i = 0; i < argc; i++) {
+ ret.emplace_back(std::string(argv[i]));
+ }
+ return ret;
+ }
+
+ static void PrintVector(std::string prefix, const std::vector<std::string> &vec) {
+ std::cerr << prefix << ": ";
+ for (auto &val: vec) {
+ std::cerr << "'" << val << "' ";
+ }
+ std::cerr << std::endl;
+ }
+
+ void Usage(std::string msg, bool exit_with_error) {
+ std::cerr << binary_name << ": ERROR: " << msg << "." << std::endl;
+ std::cerr << binary_name << ": See atf-test-program(1) for usage details." << std::endl;
+ exit(exit_with_error != 0);
+ }
+
+ // Parse args received from the OS. There can be multiple valid options:
+ // * with schebang args (#!/binary -P/path):
+ // atf_wrap '-P /path' /path/to/script -l
+ // * without schebang args
+ // atf_wrap /path/to/script -l
+ // Running test:
+ // atf_wrap '-P /path' /path/to/script -r /path1 -s /path2 -vk1=v1 testname
+ void Parse(int argc, char **argv) {
+ if (flag_debug) {
+ PrintVector("IN", ToVector(argc, argv));
+ }
+ // getopt() skips the first argument (as it is typically binary name)
+ // it is possible to have either '-P\s*/path' followed by the script name
+ // or just the script name. Parse kernel-provided arg manually and adjust
+ // array to make getopt work
+
+ binary_name = std::string(argv[0]);
+ argc--; argv++;
+ // parse -P\s*path from the kernel.
+ if (argc > 0 && !strncmp(argv[0], "-P", 2)) {
+ char *path = &argv[0][2];
+ while (*path == ' ')
+ path++;
+ python_path = std::string(path);
+ argc--; argv++;
+ }
+
+ // The next argument is a script name. Copy and keep argc/argv the same
+ // Show usage for empty args
+ if (argc == 0) {
+ Usage("Must provide a test case name", true);
+ }
+ script_path = std::string(argv[0]);
+
+ int c;
+ while ((c = getopt(argc, argv, "lr:s:v:")) != -1) {
+ switch (c) {
+ case 'l':
+ flag_list = true;
+ break;
+ case 's':
+ src_dir = std::string(optarg);
+ break;
+ case 'r':
+ dst_file = std::string(optarg);
+ break;
+ case 'v':
+ {
+ std::string kv = std::string(optarg);
+ size_t splitter = kv.find("=");
+ if (splitter == std::string::npos) {
+ Usage("Unknown variable: " + kv, true);
+ }
+ kv_map[kv.substr(0, splitter)] = kv.substr(splitter + 1);
+ }
+ break;
+ default:
+ Usage("Unknown option -" + std::string(1, static_cast<char>(c)), true);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (flag_list) {
+ return;
+ }
+ // There should be just one argument with the test name
+ if (argc != 1) {
+ Usage("Must provide a test case name", true);
+ }
+ test_name = std::string(argv[0]);
+ if (test_name.size() > kCleanupSuffix.size() &&
+ std::equal(kCleanupSuffix.rbegin(), kCleanupSuffix.rend(), test_name.rbegin())) {
+ test_name = test_name.substr(0, test_name.size() - kCleanupSuffix.size());
+ flag_cleanup = true;
+ }
+ }
+
+ std::vector<std::string> BuildArgs() {
+ std::vector<std::string> args = {"pytest", "-vv", "-p",
+ "no:cacheprovider", "-s", "--atf"};
+
+ args.push_back("--confcutdir=" + python_path);
+
+ if (flag_list) {
+ args.push_back("--co");
+ args.push_back(script_path);
+ return args;
+ }
+ if (flag_cleanup) {
+ args.push_back("--atf-cleanup");
+ }
+ // workaround pytest parser bug:
+ // https://github.com/pytest-dev/pytest/issues/3097
+ // use '--arg=value' format instead of '--arg value' for all
+ // path-like options
+ if (!src_dir.empty()) {
+ args.push_back("--atf-source-dir=" + src_dir);
+ }
+ if (!dst_file.empty()) {
+ args.push_back("--atf-file=" + dst_file);
+ }
+ // Create nodeid from the test path &name
+ args.push_back(script_path + "::" + test_name);
+ return args;
+ }
+
+ void SetPythonPath() {
+ if (!python_path.empty()) {
+ char *env_path = getenv(kPythonPathEnv.c_str());
+ if (env_path != nullptr) {
+ python_path = python_path + ":" + std::string(env_path);
+ }
+ setenv(kPythonPathEnv.c_str(), python_path.c_str(), 1);
+ }
+ }
+
+ void SetEnv() {
+ SetPythonPath();
+
+ // Pass ATF kv pairs as env variables to avoid dealing with
+ // pytest parser
+ for (auto [k, v]: kv_map) {
+ setenv((kAtfVar + k).c_str(), v.c_str(), 1);
+ }
+ }
+
+ bool Run(std::string binary, std::vector<std::string> args) {
+ if (flag_debug) {
+ PrintVector("OUT", args);
+ }
+ // allocate array with final NULL
+ char **arr = new char*[args.size() + 1]();
+ for (unsigned long i = 0; i < args.size(); i++) {
+ // work around 'char *const *'
+ arr[i] = strdup(args[i].c_str());
+ }
+ return execvp(binary.c_str(), arr) == 0;
+ }
+
+ void ReportError() {
+ if (flag_list) {
+ std::cout << "Content-Type: application/X-atf-tp; version=\"1\"";
+ std::cout << std::endl << std::endl;
+ std::cout << "ident: __test_cases_list_"<< kPytestName << "_binary_" <<
+ "not_found__" << std::endl;
+ } else {
+ std::cout << "execvp(" << kPytestName << ") failed: " <<
+ std::strerror(errno) << std::endl;
+ }
+ }
+
+ int Process() {
+ SetEnv();
+ if (!Run(kPytestName, BuildArgs())) {
+ ReportError();
+ }
+ return 0;
+ }
+};
+
+
+int main(int argc, char **argv) {
+ Handler handler;
+
+ handler.Parse(argc, argv);
+ return handler.Process();
+}
diff --git a/libexec/atf/atf-sh/Makefile b/libexec/atf/atf-sh/Makefile
new file mode 100644
index 000000000000..afd848581f36
--- /dev/null
+++ b/libexec/atf/atf-sh/Makefile
@@ -0,0 +1,80 @@
+#-
+# Copyright (c) 2011 Google, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+.include <src.opts.mk>
+.include <bsd.init.mk>
+
+ATF= ${SRCTOP}/contrib/atf
+.PATH: ${ATF}/atf-sh
+
+PROG_CXX= atf-sh
+SRCS= atf-sh.cpp
+MAN= atf-sh.1 atf-sh.3
+# Backwards compatibility.
+MLINKS+= atf-sh.3 atf-sh-api.3
+
+MLINKS+= \
+ atf-sh.3 atf_add_test_case.3 \
+ atf-sh.3 atf_check.3 \
+ atf-sh.3 atf_check_equal.3 \
+ atf-sh.3 atf_config_get.3 \
+ atf-sh.3 atf_config_has.3 \
+ atf-sh.3 atf_expect_death.3 \
+ atf-sh.3 atf_expect_exit.3 \
+ atf-sh.3 atf_expect_fail.3 \
+ atf-sh.3 atf_expect_pass.3 \
+ atf-sh.3 atf_expect_signal.3 \
+ atf-sh.3 atf_expect_timeout.3 \
+ atf-sh.3 atf_fail.3 \
+ atf-sh.3 atf_get.3 \
+ atf-sh.3 atf_get_srcdir.3 \
+ atf-sh.3 atf_init_test_cases.3 \
+ atf-sh.3 atf_pass.3 \
+ atf-sh.3 atf_require_kmod.3 \
+ atf-sh.3 atf_require_prog.3 \
+ atf-sh.3 atf_set.3 \
+ atf-sh.3 atf_skip.3 \
+ atf-sh.3 atf_test_case.3
+
+CFLAGS+= -DHAVE_CONFIG_H
+CFLAGS+= -DATF_LIBEXECDIR='"${LIBEXECDIR}"'
+CFLAGS+= -DATF_PKGDATADIR='"${SHAREDIR}/atf"'
+CFLAGS+= -DATF_SHELL='"/bin/sh"'
+CFLAGS+= -I${ATF}
+
+LIBADD= atf_cxx
+
+FILESGROUPS= SUBR
+
+SUBRDIR= ${SHAREDIR}/atf
+SUBR= libatf-sh.subr
+SUBRTAGS= package=tests
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
+.include "../../../lib/atf/common.mk"
+.include <bsd.prog.mk>
diff --git a/libexec/atf/atf-sh/Makefile.depend b/libexec/atf/atf-sh/Makefile.depend
new file mode 100644
index 000000000000..7886e7624456
--- /dev/null
+++ b/libexec/atf/atf-sh/Makefile.depend
@@ -0,0 +1,20 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/atf/libatf-c++ \
+ lib/libc \
+ lib/libc++ \
+ lib/libcompiler_rt \
+ lib/libcxxrt \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/atf/atf-sh/tests/Makefile b/libexec/atf/atf-sh/tests/Makefile
new file mode 100644
index 000000000000..d358ed403a47
--- /dev/null
+++ b/libexec/atf/atf-sh/tests/Makefile
@@ -0,0 +1,26 @@
+.include <bsd.init.mk>
+
+ATF= ${SRCTOP}/contrib/atf
+.PATH: ${ATF}/atf-sh
+
+ATF_TESTS_SH+= atf_check_test
+ATF_TESTS_SH+= config_test
+ATF_TESTS_SH+= integration_test
+ATF_TESTS_SH+= normalize_test
+ATF_TESTS_SH+= tc_test
+ATF_TESTS_SH+= tp_test
+
+integration_test: Makefile
+ATF_TESTS_SH_SED_integration_test= \
+ -e 's,__ATF_SH__,/usr/libexec/atf-sh,g'
+
+SCRIPTS+= misc_helpers
+SCRIPTSDIR_misc_helpers=${TESTSDIR}
+CLEANFILES+= misc_helpers misc_helpers.tmp
+misc_helpers: misc_helpers.sh
+ echo '#! /usr/libexec/atf-sh' >${.TARGET}.tmp
+ cat ${.ALLSRC} >>${.TARGET}.tmp
+ chmod +x ${.TARGET}.tmp
+ mv ${.TARGET}.tmp ${.TARGET}
+
+.include <bsd.test.mk>
diff --git a/libexec/atf/atf-sh/tests/Makefile.depend b/libexec/atf/atf-sh/tests/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/atf/atf-sh/tests/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/atf/tests/Makefile b/libexec/atf/tests/Makefile
new file mode 100644
index 000000000000..ad9431e75a63
--- /dev/null
+++ b/libexec/atf/tests/Makefile
@@ -0,0 +1,7 @@
+.PATH: ${SRCTOP}/tests
+
+PACKAGE= tests
+
+KYUAFILE= yes
+
+.include <bsd.test.mk>
diff --git a/libexec/atf/tests/Makefile.depend b/libexec/atf/tests/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/atf/tests/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/atrun/LEGAL b/libexec/atrun/LEGAL
new file mode 100644
index 000000000000..c6bcf3e777ab
--- /dev/null
+++ b/libexec/atrun/LEGAL
@@ -0,0 +1,30 @@
+
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Sorry for the long wait, but there still were a few things to
+be ironed out in at, which I've finally done :-)
+
+The FreeBSD team does have my permission to use at, version 2.9,
+under the BSD license.
+
+You'll find it on sunsite.unc.edu's Incoming, hopefully; the
+md5 checksum is
+
+3ba2ca3c0e87e1a04feae2c6c1376b0d at-2.9.tgz
+
+Best regards
+ Thomas
+- --
+Thomas Koenig, Thomas.Koenig@ciw.uni-karlsruhe.de, ig25@dkauni2.bitnet.
+The joy of engineering is to find a straight line on a double
+logarithmic diagram.
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.2i
+
+iQCVAwUBMCjVrPBu+cbJcKCVAQFNiQP/dpWP57s/E8plVGUD3zfgOXDmKUvg8U7a
+VwRzJrIMuSgnSJs0wkpvcomc3NLicipfX7hhWLh/xatPM2YbF7O5HZoNdvWvexD2
+1Y67zJ+0HFb1mPnSBOrS5RFiQAe3KqmGec6E14Rih/qNoFQZBVRFXZ4xxuwP+0Rs
+e2U+TVTUz6A=
+=TvyW
+-----END PGP SIGNATURE-----
diff --git a/libexec/atrun/Makefile b/libexec/atrun/Makefile
new file mode 100644
index 000000000000..7c44a8ae3d8b
--- /dev/null
+++ b/libexec/atrun/Makefile
@@ -0,0 +1,30 @@
+PACKAGE=at
+MAINSRC=${SRCTOP}/usr.bin/at
+
+.include "${MAINSRC}/Makefile.inc"
+
+PROG= atrun
+SRCS= atrun.c gloadavg.c
+MAN= atrun.8
+
+BINDIR= ${ATLIB_DIR}
+CLEANFILES= ${MAN}
+
+CFLAGS+=-I${MAINSRC} -I${.CURDIR}
+CFLAGS+=-DLOGIN_CAP -DPAM
+
+WARNS?= 2
+WFORMAT=0
+
+LIBADD= pam util
+
+atrun.8: atrun.man
+ @${ECHO} Making ${.TARGET:T} from ${.ALLSRC:T}; \
+ sed -e \
+ "s@_ATSPOOL_DIR@$(ATSPOOL_DIR)@g; \
+ s@_ATJOB_DIR@$(ATJOB_DIR)@g; \
+ s@_ATLIB_DIR@$(ATLIB_DIR)@g; \
+ s@_LOADAVG_MX@$(LOADAVG_MX)@g;" \
+ < ${.ALLSRC} > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/libexec/atrun/Makefile.depend b/libexec/atrun/Makefile.depend
new file mode 100644
index 000000000000..dcba122adac8
--- /dev/null
+++ b/libexec/atrun/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libpam/libpam \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/atrun/atrun.c b/libexec/atrun/atrun.c
new file mode 100644
index 000000000000..ee312591ccd4
--- /dev/null
+++ b/libexec/atrun/atrun.c
@@ -0,0 +1,588 @@
+/*-
+ * atrun.c - run jobs queued by at; run with root privileges.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 1993, 1994 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* System Headers */
+
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#endif
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __FreeBSD__
+#include <paths.h>
+#else
+#include <getopt.h>
+#endif
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+#ifdef PAM
+#include <security/pam_appl.h>
+#include <security/openpam.h>
+#endif
+
+/* Local headers */
+
+#include "gloadavg.h"
+#define MAIN
+#include "privs.h"
+
+/* Macros */
+
+#ifndef ATJOB_DIR
+#define ATJOB_DIR "/usr/spool/atjobs/"
+#endif
+
+#ifndef ATSPOOL_DIR
+#define ATSPOOL_DIR "/usr/spool/atspool/"
+#endif
+
+#ifndef LOADAVG_MX
+#define LOADAVG_MX 1.5
+#endif
+
+/* File scope variables */
+
+static const char * const atrun = "atrun"; /* service name for syslog etc. */
+static int debug = 0;
+
+void perr(const char *fmt, ...);
+void perrx(const char *fmt, ...);
+static void usage(void) __dead2;
+
+/* Local functions */
+static int
+write_string(int fd, const char* a)
+{
+ return write(fd, a, strlen(a));
+}
+
+#undef DEBUG_FORK
+#ifdef DEBUG_FORK
+static pid_t
+myfork(void)
+{
+ pid_t res;
+ res = fork();
+ if (res == 0)
+ kill(getpid(),SIGSTOP);
+ return res;
+}
+
+#define fork myfork
+#endif
+
+static void
+run_file(const char *filename, uid_t uid, gid_t gid)
+{
+/* Run a file by spawning off a process which redirects I/O,
+ * spawns a subshell, then waits for it to complete and sends
+ * mail to the user.
+ */
+ pid_t pid;
+ int fd_out, fd_in;
+ int queue;
+ char mailbuf[MAXLOGNAME], fmt[64];
+ char *mailname = NULL;
+ FILE *stream;
+ int send_mail = 0;
+ struct stat buf, lbuf;
+ off_t size;
+ struct passwd *pentry;
+ int fflags;
+ long nuid;
+ long ngid;
+#ifdef PAM
+ pam_handle_t *pamh = NULL;
+ int pam_err;
+ struct pam_conv pamc = {
+ .conv = openpam_nullconv,
+ .appdata_ptr = NULL
+ };
+#endif
+
+ PRIV_START
+
+ if (chmod(filename, S_IRUSR) != 0)
+ {
+ perr("cannot change file permissions");
+ }
+
+ PRIV_END
+
+ pid = fork();
+ if (pid == -1)
+ perr("cannot fork");
+
+ else if (pid != 0)
+ return;
+
+ /* Let's see who we mail to. Hopefully, we can read it from
+ * the command file; if not, send it to the owner, or, failing that,
+ * to root.
+ */
+
+ pentry = getpwuid(uid);
+ if (pentry == NULL)
+ perrx("Userid %lu not found - aborting job %s",
+ (unsigned long) uid, filename);
+
+#ifdef PAM
+ PRIV_START
+
+ pam_err = pam_start(atrun, pentry->pw_name, &pamc, &pamh);
+ if (pam_err != PAM_SUCCESS)
+ perrx("cannot start PAM: %s", pam_strerror(pamh, pam_err));
+
+ pam_err = pam_acct_mgmt(pamh, PAM_SILENT);
+ /* Expired password shouldn't prevent the job from running. */
+ if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD)
+ perrx("Account %s (userid %lu) unavailable for job %s: %s",
+ pentry->pw_name, (unsigned long)uid,
+ filename, pam_strerror(pamh, pam_err));
+
+ pam_end(pamh, pam_err);
+
+ PRIV_END
+#endif /* PAM */
+
+ PRIV_START
+
+ stream=fopen(filename, "r");
+
+ PRIV_END
+
+ if (stream == NULL)
+ perr("cannot open input file %s", filename);
+
+ if ((fd_in = dup(fileno(stream))) <0)
+ perr("error duplicating input file descriptor");
+
+ if (fstat(fd_in, &buf) == -1)
+ perr("error in fstat of input file descriptor");
+
+ if (lstat(filename, &lbuf) == -1)
+ perr("error in fstat of input file");
+
+ if (S_ISLNK(lbuf.st_mode))
+ perrx("Symbolic link encountered in job %s - aborting", filename);
+
+ if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) ||
+ (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) ||
+ (lbuf.st_size!=buf.st_size))
+ perrx("Somebody changed files from under us for job %s - aborting",
+ filename);
+
+ if (buf.st_nlink > 1)
+ perrx("Somebody is trying to run a linked script for job %s", filename);
+
+ if ((fflags = fcntl(fd_in, F_GETFD)) <0)
+ perr("error in fcntl");
+
+ fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC);
+
+ snprintf(fmt, sizeof(fmt),
+ "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d",
+ MAXLOGNAME - 1);
+
+ if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4)
+ perrx("File %s is in wrong format - aborting", filename);
+
+ if (mailbuf[0] == '-')
+ perrx("Illegal mail name %s in %s", mailbuf, filename);
+
+ mailname = mailbuf;
+
+ if (nuid != uid)
+ perrx("Job %s - userid %ld does not match file uid %lu",
+ filename, nuid, (unsigned long)uid);
+
+ if (ngid != gid)
+ perrx("Job %s - groupid %ld does not match file gid %lu",
+ filename, ngid, (unsigned long)gid);
+
+ fclose(stream);
+
+ if (chdir(ATSPOOL_DIR) < 0)
+ perr("cannot chdir to %s", ATSPOOL_DIR);
+
+ /* Create a file to hold the output of the job we are about to run.
+ * Write the mail header.
+ */
+ if((fd_out=open(filename,
+ O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0)
+ perr("cannot create output file");
+
+ write_string(fd_out, "Subject: Output from your job ");
+ write_string(fd_out, filename);
+ write_string(fd_out, "\n\n");
+ fstat(fd_out, &buf);
+ size = buf.st_size;
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ pid = fork();
+ if (pid < 0)
+ perr("error in fork");
+
+ else if (pid == 0)
+ {
+ char *nul = NULL;
+ char **nenvp = &nul;
+
+ /* Set up things for the child; we want standard input from the input file,
+ * and standard output and error sent to our output file.
+ */
+
+ if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0)
+ perr("error in lseek");
+
+ if (dup(fd_in) != STDIN_FILENO)
+ perr("error in I/O redirection");
+
+ if (dup(fd_out) != STDOUT_FILENO)
+ perr("error in I/O redirection");
+
+ if (dup(fd_out) != STDERR_FILENO)
+ perr("error in I/O redirection");
+
+ close(fd_in);
+ close(fd_out);
+ if (chdir(ATJOB_DIR) < 0)
+ perr("cannot chdir to %s", ATJOB_DIR);
+
+ queue = *filename;
+
+ PRIV_START
+
+ nice(tolower(queue) - 'a');
+
+#ifdef LOGIN_CAP
+ /*
+ * For simplicity and safety, set all aspects of the user context
+ * except for a selected subset: Don't set priority, which was
+ * set based on the queue file name according to the tradition.
+ * Don't bother to set environment, including path vars, either
+ * because it will be discarded anyway. Although the job file
+ * should set umask, preset it here just in case.
+ */
+ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL &
+ ~(LOGIN_SETPRIORITY | LOGIN_SETPATH | LOGIN_SETENV)) != 0)
+ exit(EXIT_FAILURE); /* setusercontext() logged the error */
+#else /* LOGIN_CAP */
+ if (initgroups(pentry->pw_name,pentry->pw_gid))
+ perr("cannot init group access list");
+
+ if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
+ perr("cannot change group");
+
+ if (setlogin(pentry->pw_name))
+ perr("cannot set login name");
+
+ if (setuid(uid) < 0 || seteuid(uid) < 0)
+ perr("cannot set user id");
+#endif /* LOGIN_CAP */
+
+ if (chdir(pentry->pw_dir))
+ chdir("/");
+
+ if(execle("/bin/sh","sh",(char *) NULL, nenvp) != 0)
+ perr("exec failed for /bin/sh");
+
+ PRIV_END
+ }
+ /* We're the parent. Let's wait.
+ */
+ close(fd_in);
+ close(fd_out);
+ waitpid(pid, (int *) NULL, 0);
+
+ /* Send mail. Unlink the output file first, so it is deleted after
+ * the run.
+ */
+ stat(filename, &buf);
+ if (open(filename, O_RDONLY) != STDIN_FILENO)
+ perr("open of jobfile failed");
+
+ unlink(filename);
+ if ((buf.st_size != size) || send_mail)
+ {
+ PRIV_START
+
+#ifdef LOGIN_CAP
+ /*
+ * This time set full context to run the mailer.
+ */
+ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL) != 0)
+ exit(EXIT_FAILURE); /* setusercontext() logged the error */
+#else /* LOGIN_CAP */
+ if (initgroups(pentry->pw_name,pentry->pw_gid))
+ perr("cannot init group access list");
+
+ if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
+ perr("cannot change group");
+
+ if (setlogin(pentry->pw_name))
+ perr("cannot set login name");
+
+ if (setuid(uid) < 0 || seteuid(uid) < 0)
+ perr("cannot set user id");
+#endif /* LOGIN_CAP */
+
+ if (chdir(pentry->pw_dir))
+ chdir("/");
+
+#ifdef __FreeBSD__
+ execl(_PATH_SENDMAIL, "sendmail", "-F", "Atrun Service",
+ "-odi", "-oem",
+ mailname, (char *) NULL);
+#else
+ execl(MAIL_CMD, MAIL_CMD, mailname, (char *) NULL);
+#endif
+ perr("exec failed for mail command");
+
+ PRIV_END
+ }
+ exit(EXIT_SUCCESS);
+}
+
+/* Global functions */
+
+/* Needed in gloadavg.c */
+void
+perr(const char *fmt, ...)
+{
+ const char * const fmtadd = ": %m";
+ char nfmt[strlen(fmt) + strlen(fmtadd) + 1];
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug)
+ {
+ vwarn(fmt, ap);
+ }
+ else
+ {
+ snprintf(nfmt, sizeof(nfmt), "%s%s", fmt, fmtadd);
+ vsyslog(LOG_ERR, nfmt, ap);
+ }
+ va_end(ap);
+
+ exit(EXIT_FAILURE);
+}
+
+void
+perrx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug)
+ vwarnx(fmt, ap);
+ else
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+/* Browse through ATJOB_DIR, checking all the jobfiles wether they should
+ * be executed and or deleted. The queue is coded into the first byte of
+ * the job filename, the date (in minutes since Eon) as a hex number in the
+ * following eight bytes, followed by a dot and a serial number. A file
+ * which has not been executed yet is denoted by its execute - bit set.
+ * For those files which are to be executed, run_file() is called, which forks
+ * off a child which takes care of I/O redirection, forks off another child
+ * for execution and yet another one, optionally, for sending mail.
+ * Files which already have run are removed during the next invocation.
+ */
+ DIR *spool;
+ struct dirent *dirent;
+ struct stat buf;
+ unsigned long ctm;
+ unsigned long jobno;
+ char queue;
+ time_t now, run_time;
+ char batch_name[] = "Z2345678901234";
+ uid_t batch_uid;
+ gid_t batch_gid;
+ int c;
+ int run_batch;
+#ifdef __FreeBSD__
+ size_t ncpusz;
+ double load_avg = -1;
+ int ncpu;
+#else
+ double load_avg = LOADAVG_MX;
+#endif
+
+/* We don't need root privileges all the time; running under uid and gid daemon
+ * is fine.
+ */
+
+ RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID)
+
+ openlog(atrun, LOG_PID, LOG_CRON);
+
+ opterr = 0;
+ while((c=getopt(argc, argv, "dl:"))!= -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ if (sscanf(optarg, "%lf", &load_avg) != 1)
+ perr("garbled option -l");
+#ifndef __FreeBSD__
+ if (load_avg <= 0.)
+ load_avg = LOADAVG_MX;
+#endif
+ break;
+
+ case 'd':
+ debug ++;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ if (chdir(ATJOB_DIR) != 0)
+ perr("cannot change to %s", ATJOB_DIR);
+
+#ifdef __FreeBSD__
+ if (load_avg <= 0.) {
+ ncpusz = sizeof(size_t);
+ if (sysctlbyname("hw.ncpu", &ncpu, &ncpusz, NULL, 0) < 0)
+ ncpu = 1;
+ load_avg = LOADAVG_MX * ncpu;
+ }
+#endif
+
+ /* Main loop. Open spool directory for reading and look over all the
+ * files in there. If the filename indicates that the job should be run
+ * and the x bit is set, fork off a child which sets its user and group
+ * id to that of the files and exec a /bin/sh which executes the shell
+ * script. Unlink older files if they should no longer be run. For
+ * deletion, their r bit has to be turned on.
+ *
+ * Also, pick the oldest batch job to run, at most one per invocation of
+ * atrun.
+ */
+ if ((spool = opendir(".")) == NULL)
+ perr("cannot read %s", ATJOB_DIR);
+
+ if (flock(dirfd(spool), LOCK_EX) == -1)
+ perr("cannot lock %s", ATJOB_DIR);
+
+ now = time(NULL);
+ run_batch = 0;
+ batch_uid = (uid_t) -1;
+ batch_gid = (gid_t) -1;
+
+ while ((dirent = readdir(spool)) != NULL) {
+ if (stat(dirent->d_name,&buf) != 0)
+ perr("cannot stat in %s", ATJOB_DIR);
+
+ /* We don't want directories
+ */
+ if (!S_ISREG(buf.st_mode))
+ continue;
+
+ if (sscanf(dirent->d_name,"%c%5lx%8lx",&queue,&jobno,&ctm) != 3)
+ continue;
+
+ run_time = (time_t) ctm*60;
+
+ if ((S_IXUSR & buf.st_mode) && (run_time <=now)) {
+ if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) {
+ run_batch = 1;
+ strlcpy(batch_name, dirent->d_name, sizeof(batch_name));
+ batch_uid = buf.st_uid;
+ batch_gid = buf.st_gid;
+ }
+
+ /* The file is executable and old enough
+ */
+ if (islower(queue))
+ run_file(dirent->d_name, buf.st_uid, buf.st_gid);
+ }
+ /* Delete older files
+ */
+ if ((run_time < now) && !(S_IXUSR & buf.st_mode) && (S_IRUSR & buf.st_mode))
+ unlink(dirent->d_name);
+ }
+ /* run the single batch file, if any
+ */
+ if (run_batch && (gloadavg() < load_avg))
+ run_file(batch_name, batch_uid, batch_gid);
+
+ if (flock(dirfd(spool), LOCK_UN) == -1)
+ perr("cannot unlock %s", ATJOB_DIR);
+
+ if (closedir(spool) == -1)
+ perr("cannot closedir %s", ATJOB_DIR);
+
+ closelog();
+ exit(EXIT_SUCCESS);
+}
+
+static void
+usage(void)
+{
+ if (debug)
+ fprintf(stderr, "usage: atrun [-l load_avg] [-d]\n");
+ else
+ syslog(LOG_ERR, "usage: atrun [-l load_avg] [-d]");
+
+ exit(EXIT_FAILURE);
+}
diff --git a/libexec/atrun/atrun.man b/libexec/atrun/atrun.man
new file mode 100644
index 000000000000..766953ef015d
--- /dev/null
+++ b/libexec/atrun/atrun.man
@@ -0,0 +1,83 @@
+.Dd June 22, 2015
+.Dt ATRUN 8
+.Os
+.Sh NAME
+.Nm atrun
+.Nd run jobs queued for later execution
+.Sh SYNOPSIS
+.Nm atrun
+.Op Fl l Ar load_avg
+.Op Fl d
+.Sh DESCRIPTION
+.Nm Atrun
+runs jobs queued by
+.Xr at 1 .
+.Pp
+The system
+.Xr crontab 5
+file
+.Pa /etc/cron.d/at
+must contain the line
+.Bd -literal
+*/5 * * * * root /usr/libexec/atrun
+.Ed
+.Pp
+so
+.Nm
+is invoked every five minutes.
+.Pp
+At every invocation,
+.Nm
+starts all the jobs in the lowercase queues whose start
+time has elapsed.
+In addition, if the load average over the last minute was less than
+the specified limit, then a maximum of one batch job (denoted by the
+uppercase queues) is started.
+.Pp
+Before starting a job,
+.Nm
+checks the status of its owner's account with
+.Xr pam 3
+and refuses to run the job if the account is unavailable,
+e.g., locked out or expired.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl l Ar load_avg
+Specify a limiting load factor, over which batch jobs should
+not be run, instead of the default of 1.5 * number of active CPUs.
+.It Fl d
+Debug; print error messages to standard error instead of using
+.Xr syslog 3 .
+.El
+.Sh WARNINGS
+For
+.Nm
+to work, a
+.Xr cron 8
+daemon must be running
+.Nm
+periodically.
+.Sh FILES
+.Bl -tag -width /etc/pam.d/atrun -compact
+.It Pa /etc/pam.d/atrun
+.Xr pam.conf 5
+configuration file for
+.Nm
+.It Pa /var/at/jobs
+Directory containing job files
+.It Pa /var/at/spool
+Directory containing output spool files
+.El
+.Sh SEE ALSO
+.Xr at 1 ,
+.Xr crontab 1 ,
+.Xr pam 3 ,
+.Xr syslog 3 ,
+.Xr crontab 5 ,
+.Xr pam.conf 5 ,
+.Xr cron 8
+.Sh BUGS
+The functionality of
+.Nm
+should be merged into
+.Xr cron 8 .
diff --git a/libexec/atrun/gloadavg.c b/libexec/atrun/gloadavg.c
new file mode 100644
index 000000000000..e513183a391c
--- /dev/null
+++ b/libexec/atrun/gloadavg.c
@@ -0,0 +1,69 @@
+/*-
+ * gloadavg.c - get load average for Linux
+ * Copyright (C) 1993 Thomas Koenig
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FreeBSD__
+#define _POSIX_SOURCE 1
+
+/* System Headers */
+
+#include <stdio.h>
+#else
+#include <stdlib.h>
+#endif
+
+/* Local headers */
+
+#include "gloadavg.h"
+
+/* Global functions */
+
+void perr(const char *fmt, ...);
+
+double
+gloadavg(void)
+/* return the current load average as a floating point number, or <0 for
+ * error
+ */
+{
+ double result;
+#ifndef __FreeBSD__
+ FILE *fp;
+
+ if((fp=fopen(PROC_DIR "loadavg","r")) == NULL)
+ result = -1.0;
+ else
+ {
+ if(fscanf(fp,"%lf",&result) != 1)
+ result = -1.0;
+ fclose(fp);
+ }
+#else
+ if (getloadavg(&result, 1) != 1)
+ perr("error in getloadavg");
+#endif
+ return result;
+}
diff --git a/libexec/atrun/gloadavg.h b/libexec/atrun/gloadavg.h
new file mode 100644
index 000000000000..a202cf0b3700
--- /dev/null
+++ b/libexec/atrun/gloadavg.h
@@ -0,0 +1,28 @@
+/*-
+ * gloadavg.h - header for atrun(8)
+ * Copyright (C) 1993 Thomas Koenig
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+double gloadavg(void);
diff --git a/libexec/blocklistd-helper/Makefile b/libexec/blocklistd-helper/Makefile
new file mode 100644
index 000000000000..5c72b5155662
--- /dev/null
+++ b/libexec/blocklistd-helper/Makefile
@@ -0,0 +1,10 @@
+BLOCKLIST_DIR=${SRCTOP}/contrib/blocklist
+
+PACKAGE= blocklist
+
+SCRIPTS= ${BLOCKLIST_DIR}/libexec/blocklistd-helper
+
+# blacklist
+SCRIPTS+= blacklistd-helper
+
+.include <bsd.prog.mk>
diff --git a/libexec/blocklistd-helper/Makefile.depend b/libexec/blocklistd-helper/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/blocklistd-helper/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/blocklistd-helper/blacklistd-helper b/libexec/blocklistd-helper/blacklistd-helper
new file mode 100644
index 000000000000..4195f070e8ee
--- /dev/null
+++ b/libexec/blocklistd-helper/blacklistd-helper
@@ -0,0 +1,293 @@
+#!/bin/sh
+#echo "run $@" 1>&2
+#set -x
+# $1 command
+# $2 rulename
+# $3 protocol
+# $4 address
+# $5 mask
+# $6 port
+# $7 id
+
+pf=
+if [ -f "/etc/ipfw-blacklist.rc" ]; then
+ echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
+ echo "@ WARNING: rename /etc/ipfw-blacklist.rc to @" >&2
+ echo "@ /etc/ipfw-blocklist.rc @" >&2
+ echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
+
+ pf="ipfw"
+ . /etc/ipfw-blacklist.rc
+ ipfw_offset=${ipfw_offset:-2000}
+fi
+
+if [ -z "$pf" ]; then
+ for f in npf pf ipfilter ipfw; do
+ if [ -x /etc/rc.d/$f ]; then
+ if /etc/rc.d/$f status >/dev/null 2>&1; then
+ pf="$f"
+ break
+ fi
+ elif [ -f "/etc/$f.conf" ]; then
+ # xxx assume a config file means it can be enabled --
+ # and the first one wins!
+ pf="$f"
+ break
+ fi
+ done
+fi
+
+if [ -z "$pf" -a -x "/sbin/iptables" ]; then
+ pf="iptables"
+fi
+
+if [ -z "$pf" ]; then
+ echo "$0: Unsupported packet filter" 1>&2
+ exit 1
+fi
+
+flags=
+if [ -n "$3" ]; then
+ raw_proto="$3"
+ proto="proto $3"
+ if [ $3 = "tcp" ]; then
+ flags="flags S/SAFR"
+ fi
+fi
+
+if [ -n "$6" ]; then
+ raw_port="$6"
+ port="port $6"
+fi
+
+addr="$4"
+mask="$5"
+case "$4" in
+::ffff:*.*.*.*)
+ if [ "$5" = 128 ]; then
+ mask=32
+ addr=${4#::ffff:}
+ fi;;
+esac
+
+if [ "$pf" = "pf" ]; then
+ for anchor in $(/sbin/pfctl -s Anchors 2> /dev/null); do
+ if [ "$anchor" = "blacklistd" ]; then
+ echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
+ echo "@ WARNING: rename the blacklist anchor to blocklist @" >&2
+ echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
+ fi
+ done
+fi
+
+if [ "$pf" = "ipfilter" ]; then
+ echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
+ echo "@ WARNING: blacklist has been renamed to blocklist @" >&2
+ echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
+fi
+
+case "$1" in
+add)
+ case "$pf" in
+ ipfilter)
+ # N.B.: If you reload /etc/ipf.conf then you need to stop and
+ # restart blacklistd (and make sure blacklistd_flags="-r").
+ # This should normally already be implemented in
+ # /etc/rc.d/ipfilter, but if then not add the following lines to
+ # the end of the ipfilter_reload() function:
+ #
+ # if checkyesnox blacklistd; then
+ # /etc/rc.d/blacklistd restart
+ # fi
+ #
+ # XXX we assume the following rule is present in /etc/ipf.conf:
+ # (should we check? -- it probably cannot be added dynamically)
+ #
+ # block in proto tcp/udp from any to any head blacklistd
+ #
+ # where "blacklistd" is the default rulename (i.e. "$2")
+ #
+ # This rule can come before any rule that logs connections,
+ # etc., and should be followed by final rules such as:
+ #
+ # # log all as-yet unblocked incoming TCP connection
+ # # attempts
+ # log in proto tcp from any to any flags S/SAFR
+ # # last "pass" match wins for all non-blocked packets
+ # pass in all
+ # pass out all
+ #
+ # I.e. a "pass" rule which will be the final match and override
+ # the "block". This way the rules added by blacklistd will
+ # actually block packets, and prevent logging of them as
+ # connections, because they include the "quick" flag.
+ #
+ # N.b.: $port is not included/used in rules -- abusers are cut
+ # off completely from all services!
+ #
+ # Note RST packets are not returned for blocked SYN packets of
+ # active attacks, so the port will not appear to be closed.
+ # This will probably give away the fact that a firewall has been
+ # triggered to block connections, but it prevents generating
+ # extra outbound traffic, and it may also slow down the attacker
+ # somewhat.
+ #
+ # Note also that we don't block all packets, just new attempts
+ # to open connections (see $flags above). This allows us to do
+ # counterespionage against the attacker (or continue to make use
+ # of any other services that might be on the same subnet as the
+ # supposed attacker). However it does not kill any active
+ # connections -- we rely on the reporting daemon to do its own
+ # protection and cleanup.
+ #
+ # N.B.: The rule generated here must exactly match the
+ # corresponding rule generated for the "rem" command below!
+ #
+ echo block in log quick $proto \
+ from $addr/$mask to any $flags group $2 | \
+ /sbin/ipf -A -f - >/dev/null 2>&1 && echo OK
+ ;;
+
+ ipfw)
+ # use $ipfw_offset+$port for rule number
+ rule=$(($ipfw_offset + $6))
+ tname="port$6"
+ /sbin/ipfw table $tname create type addr 2>/dev/null
+ /sbin/ipfw -q table $tname add "$addr/$mask"
+ # if rule number $rule does not already exist, create it
+ /sbin/ipfw show $rule >/dev/null 2>&1 || \
+ /sbin/ipfw add $rule drop $3 from \
+ table"("$tname")" to any dst-port $6 >/dev/null && \
+ echo OK
+ ;;
+
+ iptables)
+ if ! /sbin/iptables --list "$2" >/dev/null 2>&1; then
+ /sbin/iptables --new-chain "$2"
+ fi
+ /sbin/iptables --append INPUT --proto "$raw_proto" \
+ --dport "$raw_port" --jump "$2"
+ /sbin/iptables --append "$2" --proto "$raw_proto" \
+ --source "$addr/$mask" --dport "$raw_port" --jump DROP
+ echo OK
+ ;;
+
+ npf)
+ /sbin/npfctl rule "$2" add block in final $proto from \
+ "$addr/$mask" to any $port
+ ;;
+
+ pf)
+ # if the filtering rule does not exist, create it
+ /sbin/pfctl -a "$2/$6" -sr 2>/dev/null | \
+ grep -q "<port$6>" || \
+ echo "block in quick $proto from <port$6> to any $port" | \
+ /sbin/pfctl -a "$2/$6" -f -
+ # insert $ip/$mask into per-protocol/port anchored table
+ /sbin/pfctl -qa "$2/$6" -t "port$6" -T add "$addr/$mask" && \
+ /sbin/pfctl -qk "$addr" && echo OK
+ ;;
+
+ esac
+ ;;
+rem)
+ case "$pf" in
+ ipfilter)
+ # N.B.: The rule generated here must exactly match the
+ # corresponding rule generated for the "add" command above!
+ #
+ echo block in log quick $proto \
+ from $addr/$mask to any $flags group $2 | \
+ /sbin/ipf -A -r -f - >/dev/null 2>&1 && echo OK
+ ;;
+
+ ipfw)
+ /sbin/ipfw table "port$6" delete "$addr/$mask" 2>/dev/null && \
+ echo OK
+ ;;
+
+ iptables)
+ if /sbin/iptables --list "$2" >/dev/null 2>&1; then
+ /sbin/iptables --delete "$2" --proto "$raw_proto" \
+ --source "$addr/$mask" --dport "$raw_port" \
+ --jump DROP
+ fi
+ echo OK
+ ;;
+
+ npf)
+ /sbin/npfctl rule "$2" rem-id "$7"
+ ;;
+
+ pf)
+ /sbin/pfctl -qa "$2/$6" -t "port$6" -T delete "$addr/$mask" && \
+ echo OK
+ ;;
+
+ esac
+ ;;
+flush)
+ case "$pf" in
+ ipfilter)
+ #
+ # N.B. WARNING: This is obviously not reentrant!
+ #
+ # First we flush all the rules from the inactive set, then we
+ # reload the ones that do not belong to the group "$2", and
+ # finally we swap the active and inactive rule sets.
+ #
+ /sbin/ipf -I -F a
+ #
+ # "ipf -I -F a" also flushes active accounting rules!
+ #
+ # Note that accounting rule groups are unique to accounting
+ # rules and have nothing to do with filter rules, though of
+ # course theoretically one could use the same group name for
+ # them too.
+ #
+ # In theory anyone using any such accounting rules should have a
+ # wrapper /etc/rc.conf.d/blacklistd script (and corresponding
+ # /etc/rc.conf.d/ipfilter script) that will record and
+ # consolidate the values accumulated by such accounting rules
+ # before they are flushed, since otherwise their counts will be
+ # lost forever.
+ #
+ /usr/sbin/ipfstat -io | fgrep -v "group $2" | \
+ /sbin/ipf -I -f - >/dev/null 2>&1
+ #
+ # This MUST be done last and separately as "-s" is executed
+ # _while_ the command arguments are being processed!
+ #
+ /sbin/ipf -s && echo OK
+ ;;
+
+ ipfw)
+ /sbin/ipfw table "port$6" flush 2>/dev/null && echo OK
+ ;;
+
+ iptables)
+ if /sbin/iptables --list "$2" >/dev/null 2>&1; then
+ /sbin/iptables --flush "$2"
+ fi
+ echo OK
+ ;;
+
+ npf)
+ /sbin/npfctl rule "$2" flush
+ ;;
+
+ pf)
+ # dynamically determine which anchors exist
+ for anchor in $(/sbin/pfctl -a "$2" -s Anchors 2> /dev/null); do
+ /sbin/pfctl -a "$anchor" -t "port${anchor##*/}" -T flush
+ /sbin/pfctl -a "$anchor" -F rules
+ done
+ echo OK
+ ;;
+ esac
+ ;;
+*)
+ echo "$0: Unknown command '$1'" 1>&2
+ exit 1
+ ;;
+esac
diff --git a/libexec/bootpd/Announce b/libexec/bootpd/Announce
new file mode 100644
index 000000000000..a76e18acd2a9
--- /dev/null
+++ b/libexec/bootpd/Announce
@@ -0,0 +1,64 @@
+
+This is an enhanced version of the CMU BOOTP server which was derived
+from the original BOOTP server created by Bill Croft at Stanford.
+This version merges most of the enhancements and bug-fixes from the
+NetBSD, Columbia, and other versions.
+
+New features in version 2.4 include:
+
+ Added a simple BOOTP gateway program: bootpgw
+ Allow host name anywhere IP address is expected.
+ Automatically lookup the IP address when the name of a
+ bootptab entry is a valid hostname.
+ (Dummy entries names should start with '.')
+ Merged changes from NetBSD and Columbia versions.
+ Merged changes for Solaris-2.X and SVR4 systems.
+ Combined bootptest into the bootp release.
+ Merged tag 18 support (:ef=...:) from Jason Zions.
+ Use :ef=extension_file_name: and make the
+ extension files for all clients using bootpef.
+ Merged HP compatibility (:ra=...:) from David R Linn.
+ Allows you to override the reply address.
+ (i.e. send the reply to a broadcast address)
+ Add /etc/ethers support for NetBSD.
+ More systems support getether (Ultrix, OSF, NetBSD)
+ Added RFC 1533 tags 40,41,42
+ :yd=<NIS domain>:ys=<NIS server>:nt=<NTP server>:
+ ConvOldTab.sh to convert old (1.1) bootptab to new format.
+ Permits extended-length replies with more option data.
+
+Problems fixed in this version:
+
+ Fixed references to free host structures.
+ (used to cause core dump on Solaris)
+ Remove change that added null terminator to string options.
+ (this annoyed some clients...)
+ Add missing symbols to dump routine, fix order.
+ Works (again) with no -DSYSLOGD defined.
+ Fixed several more NULL references in readfile.
+ Added proper length checks to option insertions.
+ Fixed bootptest IP address printing.
+ Cleaned-up signed/unsigned and byteorder bugs.
+ Added SVR4/Streams support to getif and getether
+ Removed extra newlines in syslog messages.
+ Specify facility code when calling syslog(3)
+ When lookup_hwa fails, assume numeric HW address.
+
+Systems on which I have seen this code work:
+ NetBSD-1.0 (BSD-4.4 derivative)
+ SunOS 4.X (Solaris 1.X)
+ SunOS 5.X (Solaris 2.X)
+ System V/386 Rel. 4.0
+
+Systems on which others say this code works:
+ CDC EP/IX (1.4.3, 2.1.1)
+ DEC Ultrix (4.2, 4.3)
+ Linux 1.1.81
+ OSF/1 (DEC Alpha CPU)
+
+Please direct questions, comments, and bug reports to:
+ <bootp@andrew.cmu.edu>
+
+Gordon W. Ross Mercury Computer Systems
+gwr@mc.com 199 Riverneck Road
+508-256-1300 Chelmsford, MA 01824-2820
diff --git a/libexec/bootpd/Changes b/libexec/bootpd/Changes
new file mode 100644
index 000000000000..f62d89d32a48
--- /dev/null
+++ b/libexec/bootpd/Changes
@@ -0,0 +1,293 @@
+
+Changes, most recent first
+Date, <email> Real Name
+ what...
+
+--> bootp-2.4.3
+
+03/27/96 gwr@mc.com (Gordon W. Ross)
+ Use LOG_NOTICE in place of LOG_INFO for messages related
+ to unsatisfied clients [at request of <otto@tukki.jyu.fi>]
+ Fix the irix Makefile targets, and other misc.
+
+03/25/95 gwr@mc.com (Gordon W. Ross)
+ Corrected a bug I introduced into SunOS setarp, where
+ bad IP address caused "network unreachable" errors.
+ [Thanks to andrew@ntplx.net (Andrew Lindh) for the fix!]
+
+--> bootp-2.4.2
+
+01/14/95 middelin@polyware.iaf.nl (Pauline Middelink)
+ Corrected support for the Linux networking code.
+ Fixed lots of warnings (gcc -Wall)
+ Added "linux" Makefile target.
+
+01/02/95 Jukka Ukkonen <ukkonen@csc.fi>
+ Allow bootptab syntax: ha="0:0:c0:80:e8:a7"
+
+11/30/94 Tonny van Lankveld <A.L.M.G.v.Lankveld@urc.tue.nl>
+ Fix reporting of duplicate Ethernet addresses.
+
+09/06/94 longyear@netcom.com (Al Longyear)
+ Better setarp for linux, allows non-ether types.
+
+09/02/94 Robert MacKinnon <rbm@montrouge.mis.slb.com>
+ Add support for IBM's AIX 3.2.5
+
+08/30/94 piercarl@ltd.c-d.com (Piercarlo Grandi)
+ Fix select calls on linux (modifies timeval arg).
+ Fix setarp (specify Ethernet type for now).
+
+08/27/94 drew@drewsun.FEITH.COM (Andrew B. Sudell)
+ Add support for Wollongong Win-TCP (SysVr4 variant).
+
+08/24/94 gwr@mc.com (Gordon W. Ross)
+ Use sigaction() on systems that define SA_NOCLDSTOP
+ (a symbol required by POSIX) for HP/UX and others.
+
+--> bootp-2.4.1
+
+08/24/94 gwr@mc.com (Gordon W. Ross)
+ Fix bug in boot file name generation (missing init)
+
+--> bootp-2.4.0
+
+08/20/94 gwr@mc.com (Gordon W. Ross)
+ Fix code to build bootfile name based on combination of
+ client requested name and bootfile specifications.
+ Behave similarly with or without CHECK_FILE_ACCESS.
+
+07/30/94 Dirk Koeppen <dirk@incom.de>
+ Add "min wait" option (mw) to cause bootpd to ignore
+ requests from clients that have not waited long enough.
+ Add code to honor client requests containing the DHCP
+ option "Maximum Message Size" and use its value to
+ determine the size of the reply message.
+
+--> bootp-2.3.8
+
+06/25/94 Christos Zoulas <christos@deshaw.com>
+ Add "-h" flag to override host name (affects default IP
+ address provided in reply messages. (Also minor bug fix)
+
+05/27/94 gwr@mc.com (Gordon W. Ross)
+ Add code to call "arp -s IPADDR HWADDR" on systems
+ that do not provide an SIOCSARP ioctl (i.e. NetBSD)
+
+--> bootp-2.3.7
+
+05/05/94 Walter Wong <wcw+@CMU.EDU>
+ Reduce noize at debug level one, where log messages
+ are generated only for hosts that are recognized
+ and replied to by bootpd. (At request of HP folks.)
+
+04/30/94 gwr@mc.com (Gordon W. Ross)
+ Use memxxx functions unless USE_BFUNCS is defined.
+ Added -f <file> option to bootptest (requested file).
+
+04/29/94 tpaquett@ita.lgc.com (Trevor Paquette)
+ Remove call to haddr_conv802() in sendreply().
+ The setarp should get the non-transformed address.
+
+04/27/94 gwr@mc.com
+ Improve logic for building bootfile pathname, so a path
+ will be put in the reply if either the client or bootpd
+ specifies a boot file. (Needed for NetBSD diskless boot)
+
+04/25/94 shamash@boxhill.com (Ari Shamash)
+ Fix prs_inetaddr() so it allows '_' in hostnames.
+
+04/16/94 gwr@mc.com (Gordon W. Ross)
+ Fix setarp for SVR4 (needs to use I_STR ioctl)
+ Thanks to several people: (all sent the same fix)
+ Barney Wolff <barney@databus.com>,
+ bear@upsys.se (Bj|rn Sj|holm),
+ Michael Kuschke <Michael.Kuschke@Materna.DE>,
+
+03/25/95 Ulrich Heuer </I=zhhi9/G=Ulrich/S=Heuer/@zhflur.ubs.ubs.ch>
+ Make option string lengths not include a null terminator.
+ The trailing null breaks some clients.
+
+03/15/94 "Edmund J. Sutcliffe" <ejs1@tower.york.ac.uk>
+ Add support for the "EX" option: Execute a program
+ before sending a BOOTREPLY to a client. Support for
+ this option is conditional on YORK_EX_OPTION.
+
+03/10/94 Nigel Metheringham <nigelm@ohm.york.ac.uk>
+ Make getether.c work on Linux.
+
+03/09/94 Koch@Math.Uni-Duisburg.DE (Peter Koch)
+ Add missing MANDIR definition to Makefile.
+
+03/08/94 Jeroen.Scheerder@let.ruu.nl
+ Fix args to report in getether code for Ultrix.
+ Run install individually for each program.
+
+--> bootp-2.3.6
+03/07/94 gwr@mc.com
+ Cleanup for release (run gnu indent, tab-size=4)
+
+02/24/94 Jeroen.Scheerder@let.ruu.nl
+ Allow underscore in host names - readfile.c:goodname()
+ Add ConvOldTab.sh - converts 1.1 bootptab to new format.
+
+02/20/94 gwr@mc.com (Gordon W. Ross)
+ Make readfile tolerant of hardware addresses that start
+ with a letter. (If lookup_hwa() fails, assume numeric.)
+ Fix whitespace skip before :vm= auto: and avoid lookup.
+
+02/12/94 walker@zk3.dec.com (Mary Walker)
+ Added support for 64-bit longs (for the DEC Alpha)
+ Allow ieee802 hardware address in bit-reversed oreder
+
+02/07/94 hl@tekla.fi (Harald Lundberg)
+ Fix conflict with DUMP_FILE in syslog.h on OSF1
+ Use int for (struct bootp).bp_xid (for DEC Alpha)
+ Added Ultrix support to bootptest (getether)
+
+02/06/94 brezak@ch.hp.com (John Brezak)
+ Add man-page and install targets to Makefile.NetBSD
+ Add getether support for NetBSD
+
+02/05/94 gwr@mc.com (Gordon W. Ross)
+ Added tags 40,41,42 (NIS domain, NIS server, NTP server)
+ Add stub to getether for machines not yet supported.
+
+--> bootp-2.3.5
+01/29/94 gwr@mc.com (Gordon W. Ross)
+ Make bootpgw put a correct address in "giaddr" when
+ the client request came via broadcast.
+
+01/22/94 gwr@mc.com (Gordon W. Ross)
+ Fix syslog call (missing "facility" code)
+ Add SVR4/Streams support to getif() and getether()
+ Fix getif bug (matched when it should not)
+ Macro-ize lots of similar cases in readfile.c
+
+12/27/93 brezak@ch.hp.com (John Brezak)
+ Remove all newlines passed to syslog(3)
+ Add /etc/ethers support for NetBSD.
+
+12/18/93 gwr@mc.com (Gordon W. Ross)
+ Fix bootptest IP address printing.
+ Fix byte-order bugs in bootpgw and bootptest.
+ Clean-up signed/unsigned mismatches.
+ Back out SLIP support changes for now
+ (code fragment saved in ToDo).
+
+--> bootp-2.3.4 (beta test release)
+12/12/93 gwr@mc.com (Gordon W. Ross)
+ Fixed several more NULL references in readfile.
+ Added proper length checks to option insertions.
+
+--> bootp-2.3.3 (beta test release)
+12/09/93 gwr@mc.com (Gordon W. Ross)
+ Added ASSERT checks to readfile.c:fill_defaults()
+
+12/08/93 brezak@ch.hp.com (John Brezak)
+ New Makefile.NetBSD
+ Added setsid() and #ifdef TIOCNOTTY
+ (bootpd.c, bootpgw.c)
+ Moved #include <net/if.h> out of #ifdef SUNOS
+ Fixed several multiple declaration problems
+
+12/04/93 gwr@mc.com (Gordon W. Ross)
+ Re-implemented Extension File support
+ based on work by Jason Zions <jazz@hal.com>
+ Added support for Reply-Address-Override to support
+ HP clients (need reply sent to broadcast address)
+ from David R. Linn <drl@vuse.vanderbilt.edu>
+
+--> bootp-2.3.2 (beta test release)
+11/27/93 gwr@mc.com (Gordon W. Ross)
+ Incorporated bootptest into the bootp release.
+ Added ANSI function prototypes everywhere.
+
+11/17/93 dpm@depend.com (David P. Maynard)
+ Added automatic SLIP address determination.
+ (This is NOT dynamic IP address assignment.)
+ Cleaned up some type warnings from gcc.
+
+11/11/93 gwr@mc.com (Gordon W. Ross)
+ Works (again) with no -DSYSLOGD defined.
+ Provide a default value for the subnet mask.
+ More #ifdef's for SunOS specific code (lookup_hwa)
+ Added a simple BOOTP gateway program: bootpgw
+ Reorganized for more code sharing (with bootpgw)
+
+--> bootp-2.3.1 (alpha test release)
+11/08/93 gwr@mc.com (Gordon W. Ross)
+ Back-out changes to honor option structure in request
+ (this needs to be a per-client option).
+ Merged changes from NetBSD and Columbia versions.
+ Allow host name anywhere IP address is expected.
+ Add null terminators to option strings.
+ Add missing symbols to dump routine, dump symbols
+ in alphabetical order, one tag per line.
+
+--> bootp-2.2.D (posted as patch 2)
+10/19/93 gwr@mc.com (Gordon W. Ross)
+ Fix references to free memory (leads to core dumps).
+
+--> bootp-2.2.C (posted as patch 1)
+10/14/93 gwr@mc.com (Gordon W. Ross)
+ Fix data access alignment problems on SPARC/Solaris.
+
+--> bootp-2.2.B (posted to usenet)
+10/11/93 gwr@mc.com (Gordon W. Ross)
+ Allow extended-length BOOTP packets (more vendor options)
+ Honor option format specified in client requests.
+ Added Solaris-2.X changes from db@sunbim.be (Danny Backx).
+
+All history before this point may be inaccurate. Please send
+changes if any of the credits are incorrect. -gwr
+
+--> bootp-2.2+NetBSD released
+08/27/93 brezak@ch.hp.com (John Brezak)
+ Added RFC 1396 support (tags 14-17)
+
+--> bootp-2.2+NetBSD (version?)
+??/??/93 mckim@lerc.nasa.gov (Jim McKim)
+ Ported to NetBSD (see Makefile.NetBSD)
+ Set server host name in responses.
+ Check all interfaces in address match routine.
+
+--> bootp-2.2+FdC released
+01/27/93 <fdc@watsun.cc.columbia.edu> Frank da Cruz
+ Added RFC 1395 information: Merit dump file,
+ client domain name, swap server address, root path.
+
+--> bootp-2.2alpha released
+11/14/91 <walt+@cmu.edu> Walter L. Wimer
+ Add "td" to TFTP directory for "secure" (chroot) TFTP.
+ Add "sa" tag to set explicit server address.
+ Automatically determine if child of inetd.
+ Use RFC 1048 format when request has magic number zero.
+ Fixed various bugs. Give bootptab a separate man page.
+
+--> bootp-2.1 released
+01/09/89 <walt+@cmu.edu> Walter L. Wimer
+ Check world read bit on TFTP boot file.
+ Add support for rfc1085 "bootfile size" tag.
+ Add generic tags. Fix byte order of rfc1048 data.
+ Fix various crashing bugs.
+
+--> bootp-2.0 released
+07/15/88 <walt+@cmu.edu> Walter L. Wimer
+ Added vendor information to conform to RFC1048.
+ Adopted termcap-like file format to support above.
+ Added hash table lookup instead of linear search.
+ Other cleanups.
+
+--> bootp-1.3(?) released
+07/24/87 <ddp@andrew.cmu.edu> Drew D. Perkins
+ Modified to use syslog instead of Kovar's
+ routines. Add debugging dumps. Many other fixups.
+
+--> bootp-1.2(?) released
+07/30/86 David Kovar at Carnegie Mellon University
+ Modified to work at CMU.
+
+--> bootp-1.1 released
+01/22/86 Bill Croft at Stanford University
+ Original created.
diff --git a/libexec/bootpd/ConvOldTab.sh b/libexec/bootpd/ConvOldTab.sh
new file mode 100755
index 000000000000..00683f0c049c
--- /dev/null
+++ b/libexec/bootpd/ConvOldTab.sh
@@ -0,0 +1,141 @@
+#!/bin/sh
+# convert_bootptab Jeroen.Scheerder@let.ruu.nl 02/25/94
+# This script can be used to convert bootptab files in old format
+# to new (termcap-like) bootptab files
+#
+# The old format - real entries are commented out by '###'
+#
+# Old-style bootp files consist of two sections.
+# The first section has two entries:
+# First, a line that specifies the home directory
+# (where boot file paths are relative to)
+
+###/tftpboot
+
+# The next non-empty non-comment line specifies the default bootfile
+
+###no-file
+
+# End of first section - indicated by '%%' at the start of the line
+
+###%%
+
+# The remainder of this file contains one line per client
+# interface with the information shown by the table headings
+# below. The host name is also tried as a suffix for the
+# bootfile when searching the home directory (that is,
+# bootfile.host)
+#
+# Note that htype is always 1, indicating the hardware type Ethernet.
+# Conversion therefore always yields ':ha=ether:'.
+#
+# host htype haddr iaddr bootfile
+#
+
+###somehost 1 00:0b:ad:01:de:ad 128.128.128.128 dummy
+
+# That's all for the description of the old format.
+# For the new-and-improved format, see bootptab(5).
+
+set -u$DX
+
+case $#
+in 2 ) OLDTAB=$1 ; NEWTAB=$2 ;;
+ * ) echo "Usage: `basename $0` <Input> <Output>"
+ exit 1
+esac
+
+if [ ! -r $OLDTAB ]
+then
+ echo "`basename $0`: $OLDTAB does not exist or is unreadable."
+ exit 1
+fi
+
+if touch $NEWTAB 2> /dev/null
+then
+ :
+else
+ echo "`basename $0`: cannot write to $NEWTAB."
+ exit 1
+fi
+
+
+cat << END_OF_HEADER >> $NEWTAB
+# /etc/bootptab: database for bootp server (/etc/bootpd)
+# This file was generated automagically
+
+# Blank lines and lines beginning with '#' are ignored.
+#
+# Legend: (see bootptab.5)
+# first field -- hostname (not indented)
+# bf -- bootfile
+# bs -- bootfile size in 512-octet blocks
+# cs -- cookie servers
+# df -- dump file name
+# dn -- domain name
+# ds -- domain name servers
+# ef -- extension file
+# gw -- gateways
+# ha -- hardware address
+# hd -- home directory for bootfiles
+# hn -- host name set for client
+# ht -- hardware type
+# im -- impress servers
+# ip -- host IP address
+# lg -- log servers
+# lp -- LPR servers
+# ns -- IEN-116 name servers
+# ra -- reply address
+# rl -- resource location protocol servers
+# rp -- root path
+# sa -- boot server address
+# sm -- subnet mask
+# sw -- swap server
+# tc -- template host (points to similar host entry)
+# td -- TFTP directory
+# to -- time offset (seconds)
+# ts -- time servers
+# vm -- vendor magic number
+# Tn -- generic option tag n
+#
+# Be careful about including backslashes where they're needed. Weird (bad)
+# things can happen when a backslash is omitted where one is intended.
+# Also, note that generic option data must be either a string or a
+# sequence of bytes where each byte is a two-digit hex value.
+
+# First, we define a global entry which specifies the stuff every host uses.
+# (Host name lookups are relative to the domain: your.domain.name)
+
+END_OF_HEADER
+
+# Fix up HW addresses in aa:bb:cc:dd:ee:ff and aa-bb-cc-dd-ee-ff style first
+# Then awk our stuff together
+sed -e 's/[:-]//g' < $OLDTAB | \
+nawk 'BEGIN { PART = 0 ; FIELD=0 ; BOOTPATH="unset" ; BOOTFILE="unset" }
+ /^%%/ {
+ PART = 1
+ printf ".default:\\\n\t:ht=ether:\\\n\t:hn:\\\n\t:dn=your.domain.name:\\\n\t:ds=your,dns,servers:\\\n\t:sm=255.255.0.0:\\\n\t:hd=%s:\\\n\t:rp=%s:\\\n\t:td=%s:\\\n\t:bf=%s:\\\n\t:to=auto:\n\n", BOOTPATH, BOOTPATH, BOOTPATH, BOOTFILE
+ next
+ }
+ /^$/ { next }
+ /^#/ { next }
+ {
+ if ( PART == 0 && FIELD < 2 )
+ {
+ if ( FIELD == 0 ) BOOTPATH=$1
+ if ( FIELD == 1 ) BOOTFILE=$1
+ FIELD++
+ }
+ }
+ {
+ if ( PART == 1 )
+ {
+ HOST=$1
+ HA=$3
+ IP=$4
+ BF=$5
+ printf "%s:\\\n\t:tc=.default:\\\n\t:ha=0x%s:\\\n\t:ip=%s:\\\n\t:bf=%s:\n", HOST, HA, IP, BF
+ }
+ }' >> $NEWTAB
+
+exit 0
diff --git a/libexec/bootpd/Installation b/libexec/bootpd/Installation
new file mode 100644
index 000000000000..466cabce0cdb
--- /dev/null
+++ b/libexec/bootpd/Installation
@@ -0,0 +1,29 @@
+
+Installation instructions for SunOS
+
+Compile the executable:
+For SunOS 4.X:
+ make sunos4
+For SunOS 5.X: (Solaris)
+ make sunos5
+
+Install the executables:
+
+ make install
+
+Edit (or create) the bootptab:
+(See bootptab.sample and bootptab.5 manual entry)
+ edit /etc/bootptab
+
+Edit /etc/services to add these two lines:
+bootps 67/udp bootp # BOOTP Server
+bootpc 68/udp # BOOTP Client
+
+Edit /etc/inetd.conf to add the line:
+bootp dgram udp wait root /usr/etc/bootpd bootpd -i
+
+If you compiled report.c with LOG_LOCAL2 (defined in the Makefile)
+then you may want to capture syslog messages from BOOTP by changing
+your syslog.conf file. (See the sample syslog.conf file here).
+Test the change with: logger -t test -p local2.info "message"
+
diff --git a/libexec/bootpd/Makefile b/libexec/bootpd/Makefile
new file mode 100644
index 000000000000..7d07cac1cc6c
--- /dev/null
+++ b/libexec/bootpd/Makefile
@@ -0,0 +1,17 @@
+# bootpd/Makefile
+
+PROG= bootpd
+CFLAGS+= -DETC_ETHERS
+CFLAGS+= -DSYSLOG -DDEBUG -DVEND_CMU
+
+WARNS?= 2
+
+SUBDIR= bootpgw tools
+
+SRCS= bootpd.c dovend.c readfile.c hash.c dumptab.c \
+ lookup.c getif.c hwaddr.c report.c tzone.c rtmsg.c
+
+MAN= bootptab.5 bootpd.8
+MLINKS= bootpd.8 bootpgw.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/bootpd/Makefile.UNIX b/libexec/bootpd/Makefile.UNIX
new file mode 100644
index 000000000000..701eb2bcc106
--- /dev/null
+++ b/libexec/bootpd/Makefile.UNIX
@@ -0,0 +1,203 @@
+#
+# Makefile for the BOOTP programs:
+# bootpd - BOOTP server daemon
+# bootpef - BOOTP extension file builder
+# bootpgw - BOOTP gateway daemon
+# bootptest - BOOTP tester (client)
+#
+
+# OPTion DEFinitions:
+# Remove the -DVEND_CMU if you don't wish to support the "CMU vendor format"
+# in addition to the RFC1048 format. Leaving out DEBUG saves little.
+OPTDEFS= -DSYSLOG -DVEND_CMU -DDEBUG
+
+# Uncomment and edit this to choose the facility code used for syslog.
+# LOG_FACILITY= "-DLOG_BOOTP=LOG_LOCAL2"
+
+# SYStem DEFinitions:
+# Either uncomment some of the following, or do:
+# "make sunos4" (or "make sunos5", etc.)
+# SYSDEFS= -DSUNOS -DETC_ETHERS
+# SYSDEFS= -DSVR4
+# SYSLIBS= -lsocket -lnsl
+
+# Uncomment this if your system does not provide streror(3)
+# STRERROR=strerror.o
+
+# FILE DEFinitions:
+# The next few lines may be uncommented and changed to alter the default
+# filenames bootpd uses for its configuration and dump files.
+#CONFFILE= -DCONFIG_FILE=\"/usr/etc/bootptab\"
+#DUMPFILE= -DDUMPTAB_FILE=\"/usr/etc/bootpd.dump\"
+#FILEDEFS= $(CONFFILE) $(DUMPFILE)
+
+# MORE DEFinitions (whatever you might want to add)
+# One might define NDEBUG (to remove "assert()" checks).
+MOREDEFS=
+
+INSTALL=/usr/bin/install
+DESTDIR=
+BINDIR=/usr/etc
+MANDIR=/usr/local/man
+
+CFLAGS= $(OPTDEFS) $(SYSDEFS) $(FILEDEFS) $(MOREDEFS)
+PROGS= bootpd bootpef bootpgw bootptest
+TESTS= trylook trygetif trygetea
+
+all: $(PROGS) $(TESTS)
+
+system: install
+
+install: $(PROGS)
+ -for f in $(PROGS) ;\
+ do \
+ $(INSTALL) -c -s $$f $(DESTDIR)$(BINDIR) ;\
+ done
+
+MAN5= bootptab.5
+MAN8= bootpd.8 bootpef.8 bootptest.8
+install.man: $(MAN5) $(MAN8)
+ -for f in $(MAN5) ;\
+ do \
+ $(INSTALL) -c -m 644 $$f $(DESTDIR)$(MANDIR)/man5 ;\
+ done
+ -for f in $(MAN8) ;\
+ do \
+ $(INSTALL) -c -m 644 $$f $(DESTDIR)$(MANDIR)/man8 ;\
+ done
+
+clean:
+ -rm -f core *.o
+ -rm -f $(PROGS) $(TESTS)
+
+distclean:
+ -rm -f *.BAK *.CKP *~ .emacs*
+
+#
+# Handy targets for systems needing special treatment:
+# (Most POSIX systems should work with just "make all")
+#
+
+# DEC/OSF1 on the Alpha
+alpha:
+ $(MAKE) SYSDEFS="-DETC_ETHERS -Dint32=int -D_SOCKADDR_LEN" \
+ STRERROR=strerror.o
+
+# Control Data EP/IX 1.4.3 system, BSD 4.3 mode
+epix143:
+ $(MAKE) CC="cc -systype bsd43" \
+ SYSDEFS="-Dconst= -D_SIZE_T -DNO_UNISTD -DUSE_BFUNCS" \
+ STRERROR=strerror.o
+
+# Control Data EP/IX 2.1.1 system, SVR4 mode
+epix211:
+ $(MAKE) CC="cc -systype svr4" \
+ SYSDEFS="-DSVR4" \
+ SYSLIBS="-lsocket -lnsl"
+
+# IRIX 5.X (Silicon Graphics)
+irix:
+ $(MAKE) SYSDEFS= SYSLIBS=
+
+# Linux 1.1.80+ on [34]86
+linux:
+ $(MAKE) SYSDEFS="-O6 -Wall -fomit-frame-pointer"
+
+# SunOS 4.X
+sunos4:
+ $(MAKE) SYSDEFS="-DSUNOS -DETC_ETHERS" \
+ STRERROR=strerror.o
+
+# Solaris 2.X (i.e. SunOS 5.X)
+sunos5:
+ $(MAKE) SYSDEFS="-DSVR4 -DETC_ETHERS" \
+ SYSLIBS="-lsocket -lnsl"
+
+# Solaris 2.X (i.e. SunOS 5.X) with GCC. Note that GCC normally
+# defines __STDC__=1 which breaks many Solaris header files...
+sunos5gcc:
+ $(MAKE) SYSDEFS="-DSVR4 -DETC_ETHERS -D__STDC__=0" \
+ SYSLIBS="-lsocket -lnsl" CC="gcc -Wall"
+
+# UNIX System V Rel. 3
+svr3:
+ $(MAKE) SYSDEFS="-DSYSV"
+
+# UNIX System V Rel. 4
+svr4:
+ $(MAKE) SYSDEFS="-DSVR4" \
+ SYSLIBS="-lsocket -lnsl"
+
+# AT&T/GIS - Both AT&T StarServer and NCR 3000
+# may work for others using Wollongong's WIN-TCP
+wollongong gis :
+ $(MAKE) SYSDEFS="-DSVR4 -DWIN_TCP" \
+ SYSLIBS="-lsocket -lnsl"
+
+#
+# How to build each program:
+#
+
+OBJ_D= bootpd.o dovend.o readfile.o hash.o dumptab.o \
+ lookup.o getif.o hwaddr.o tzone.o report.o $(STRERROR)
+bootpd: $(OBJ_D)
+ $(CC) -o $@ $(OBJ_D) $(SYSLIBS)
+
+OBJ_EF= bootpef.o dovend.o readfile.o hash.o dumptab.o \
+ lookup.o hwaddr.o tzone.o report.o $(STRERROR)
+bootpef: $(OBJ_EF)
+ $(CC) -o $@ $(OBJ_EF) $(SYSLIBS)
+
+OBJ_GW= bootpgw.o getif.o hwaddr.o report.o $(STRERROR)
+bootpgw: $(OBJ_GW)
+ $(CC) -o $@ $(OBJ_GW) $(SYSLIBS)
+
+OBJ_TEST= bootptest.o print-bootp.o getif.o getether.o \
+ report.o $(STRERROR)
+bootptest: $(OBJ_TEST)
+ $(CC) -o $@ $(OBJ_TEST) $(SYSLIBS)
+
+# This is just for testing the lookup functions.
+TRYLOOK= trylook.o lookup.o report.o $(STRERROR)
+trylook : $(TRYLOOK)
+ $(CC) -o $@ $(TRYLOOK) $(SYSLIBS)
+
+# This is just for testing getif.
+TRYGETIF= trygetif.o getif.o report.o $(STRERROR)
+trygetif : $(TRYGETIF)
+ $(CC) -o $@ $(TRYGETIF) $(SYSLIBS)
+
+# This is just for testing getether.
+TRYGETEA= trygetea.o getether.o report.o $(STRERROR)
+trygetea : $(TRYGETEA)
+ $(CC) -o $@ $(TRYGETEA) $(SYSLIBS)
+
+# This rule just keeps the LOG_BOOTP define localized.
+report.o : report.c
+ $(CC) $(CFLAGS) $(LOG_FACILITY) -c $<
+
+# Punt SunOS -target noise
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+#
+# Header file dependencies:
+#
+
+bootpd.o : bootp.h bptypes.h hash.h hwaddr.h bootpd.h dovend.h
+bootpd.o : readfile.h report.h tzone.h patchlevel.h getif.h
+bootpef.o : bootp.h bptypes.h hash.h hwaddr.h bootpd.h dovend.h
+bootpef.o : readfile.h report.h tzone.h patchlevel.h
+bootpgw.o : bootp.h bptypes.h getif.h hwaddr.h report.h patchlevel.h
+bootptest.o : bootp.h bptypes.h bootptest.h getif.h patchlevel.h
+dovend.o : bootp.h bptypes.h bootpd.h hash.h hwaddr.h report.h dovend.h
+dumptab.o : bootp.h bptypes.h hash.h hwaddr.h report.h patchlevel.h bootpd.h
+getif.o : getif.h report.h
+hash.o : hash.h
+hwaddr.o : bptypes.h hwaddr.h report.h
+lookup.o : bootp.h bptypes.h lookup.h report.h
+print-bootp.o : bootp.h bptypes.h bootptest.h
+readfile.o : bootp.h bptypes.h hash.h hwaddr.h lookup.h readfile.h
+readfile.o : report.h tzone.h bootpd.h
+report.o : report.h
+tzone.o : bptypes.h report.h tzone.h
diff --git a/libexec/bootpd/Makefile.depend b/libexec/bootpd/Makefile.depend
new file mode 100644
index 000000000000..344a5d0e9310
--- /dev/null
+++ b/libexec/bootpd/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/bootpd/Makefile.inc b/libexec/bootpd/Makefile.inc
new file mode 100644
index 000000000000..338eb907e37d
--- /dev/null
+++ b/libexec/bootpd/Makefile.inc
@@ -0,0 +1,3 @@
+WARNS?= 1
+
+.include "../Makefile.inc"
diff --git a/libexec/bootpd/Problems b/libexec/bootpd/Problems
new file mode 100644
index 000000000000..78d809e6b215
--- /dev/null
+++ b/libexec/bootpd/Problems
@@ -0,0 +1,65 @@
+
+Common problems and ways to work around them:
+
+Bootpd complains: "bind: Address already in use" and fails to start.
+ You are already running something that has bound the
+ BOOTP listening port number. Check /etc/inetd.conf or
+ the equivalent for a bootp line (or in startup files).
+
+Bootpd complains that it "can not get IP addr for HOSTNAME"
+
+ If the entry is a "dummy" (not a real host) used only for
+ reference by other entries, put '.' in front of the name.
+
+ If the entry is for a real client and the IP address for
+ the client can not be found using gethostbyname(), specify
+ the IP address for the client using numeric form.
+
+Bootpd takes a long time to finish parsing the bootptab file:
+
+ Excessive startup time is usually caused by waiting for
+ timeouts on failed DNS lookup operations. If this is the
+ problem, find the client names for which DNS lookup fails
+ and change the bootptab to specify the IP addresses for
+ those clients using numeric form.
+
+ When bootptab entries do not specify an ip address, bootpd
+ attempts to lookup the tagname as a host name to find the
+ IP address. To suppress this default action, either make
+ the entry a "dummy" or specify its IP numeric address.
+
+ If your DNS lookups work but are just slow, consider either
+ running bootpd on the same machine as the DNS server or
+ running a caching DNS server on the host running bootpd.
+
+My huge bootptab file causes startup time to be so long that clients
+give up waiting for a reply.
+
+ Truly huge bootptab files make "inetd" mode impractical.
+ Start bootpd in "standalone" mode when the server boots.
+
+ Another possibility is to run one bootpd on each network
+ segment so each one can have a smaller bootptab. Only one
+ instance of bootpd may run on one server, so you would need
+ to use a different server for each network segment.
+
+My bootp clients are given responses with a boot file name that is
+not a fully specified path.
+
+ Make sure the TFTP directory or home directory tags are set:
+ :td=/tftpboot: (or)
+ :hd=/usr/boot: (for example)
+
+My PC clients running Sun's PC-NFS Pro v1.1 fail to receive
+acceptable responses from the bootp server.
+
+ These clients send a request with the DHCP "message length"
+ option and the (new) BOOTP "broadcast flag" both set.
+ The bootp server (on SunOS) will send a fragmented reply
+ unless you override the length with :ms=1024: (or less).
+ The "broadcast flag" is not yet supported, but there is
+ a simple work-around, just add :ra=255.255.255.255:
+ for any clients that need their reply broadcasted.
+ You may need to use a differnet broadcast address.
+ (Thanks to Ivan Auger <ivan.auger@wadsworth.org>)
+
diff --git a/libexec/bootpd/README b/libexec/bootpd/README
new file mode 100644
index 000000000000..0901b2578598
--- /dev/null
+++ b/libexec/bootpd/README
@@ -0,0 +1,135 @@
+
+This is an enhanced version of the CMU BOOTP server which was derived
+from the original BOOTP server created by Bill Croft at Stanford.
+This version merges all the enhancements and bug-fixes from the
+NetBSD, Columbia, and other versions.
+
+Please direct questions, comments, and bug reports to the list:
+ <bootp@andrew.cmu.edu>
+
+You can subscribe to this mailing list by sending mail to:
+ bootp-request@andrew.cmu.edu
+(The body of the message should contain: "Add <your-address>")
+
+[ From the NetBSD README file: ]
+
+BOOTPD is a useful adjunct to the nfs diskless boot EPROM code.
+
+The alternatives for initiating a boot of a kernel across a network
+are to use RARP protocol, or BOOTP protocol. BOOTP is more flexible;
+it allows additional items of information to be returned to the
+booting client; it also supports booting across gateways.
+
+[ From the CMU README file: ]
+
+Notes:
+1) BOOTP was originally designed and implemented by Bill Croft at Stanford.
+ Much of the credit for the ideas and the code goes to him. We've added
+ code to support the vendor specific area of the packet as specified in
+ RFC1048. We've also improved the host lookup algorithm and added some
+ extra logging.
+
+2) The server now uses syslog to do logging. Specifically it uses the 4.3bsd
+ version. I've #ifdef'd all of these calls. If you are running 4.2 you
+ should compile without the -DSYSLOG switch.
+
+3) You must update your /etc/services file to contain the following two lines:
+ bootps 67/udp bootp # BOOTP Server
+ bootpc 68/udp # BOOTP Client
+
+4) Edit the bootptab. It has some explanitory comments, and there
+ is a manual entry describing its format (bootptab.5)
+ If you have any questions, just let us know.
+
+Construction:
+ [ See the file Installation which is more up-to-date. -gwr ]
+
+ Make sure all of the files exist first. If anything is missing,
+ please contact either Walt Wimer or Drew Perkins by E-mail or phone.
+ Addresses and phone numbers are listed below.
+
+ Type 'make'. The options at present are: -DSYSLOG which enables logging
+ code, -DDEBUG which enables table dumping via signals, and -DVEND_CMU
+ which enables the CMU extensions for CMU PC/IP.
+
+ Edit the bootptab. The man page and the comments in the file should
+ explain how to go about doing so. If you have any problems, let me know.
+
+ Type 'make install'. This should put all of the files in the right place.
+
+ Edit your /etc/rc.local or /etc/inetd.conf file to start up bootpd upon
+ reboot. The following is a sample /etc/inetd.conf entry:
+ # BOOTP server
+ bootps dgram udp wait root /usr/etc/bootpd bootpd -i
+
+Care and feeding:
+ If you change the interface cards on your host or add new hosts you will
+ need to update /etc/bootptab. Just edit it as before. Once you write
+ it back out, bootpd will notice that there is a new copy and will
+ reread it the next time it gets a request.
+
+ If your bootp clients don't get a response then several things might be
+ wrong. Most often, the entry for that host is not in the database.
+ Check the hardware address and then check the entry and make sure
+ everything is right. Other problems include the server machine crashing,
+ bad cables, and the like. If your network is very congested you should
+ try making your bootp clients send additional requests before giving up.
+
+
+November 7, 1988
+
+
+Walter L. Wimer Drew D. Perkins
+ww0n@andrew.cmu.edu ddp@andrew.cmu.edu
+(412) 268-6252 (412) 268-8576
+
+4910 Forbes Ave
+Pittsburgh, PA 15213
+
+[ Contents description by file: ]
+
+Announce* Text of release announcements
+Changes Change history, reverse chronological
+ConvOldTab.sh Script to convert old (1.x) bootptab files
+Installation Instructions for building and installing
+Makefile* for "make"
+README This file
+ToDo Things not yet done
+bootp.h The protocol header file
+bootpd.8 Manual page for bootpd, boopgw
+bootpd.c BOOTP server main module
+bootpd.h header for above (and others)
+bootpef.8 Manual page for bootpef
+bootpef.c BOOTP extension file compiler
+bootpgw.c BOOTP gateway main module
+bootptab.5 A manual describing the bootptab format
+bootptab.cmu A sample database file for the server
+bootptab.mcs Another sample from <gwr@mc.com>
+bootptest.8 Manual page for bootptest
+bootptest.c BOOTP test program (fake client)
+bootptest.h header for above
+dovend.c Vendor Option builder (for bootpd, bootpef)
+dovend.h header for above
+dumptab.c Implements debugging dump for bootpd
+getether.c For bootptest (not used yet)
+getether.h header for above
+getif.c Get network interface info.
+getif.h header for above
+hash.c The hash table module
+hash.h header for above
+hwaddr.c Hardware address support
+hwaddr.h header for above
+lookup.c Internet Protocol address lookup
+lookup.h header for above
+patchlevel.h Holds version numbers
+print-bootp.c Prints BOOTP packets (taken from BSD tcpdump)
+readfile.c The configuration file-reading routines
+readfile.h header for above
+report.c Does syslog-style messages
+report.h header for above
+strerror.c Library errno-to-string (for systems lacking it)
+syslog.conf Sample config file for syslogd(8)
+syslog.h For systems that lack syslog(3)
+try*.c Test programs (for debugging)
+tzone.c Get timezone offset
+tzone.h header for above
diff --git a/libexec/bootpd/ToDo b/libexec/bootpd/ToDo
new file mode 100644
index 000000000000..261d24c72695
--- /dev/null
+++ b/libexec/bootpd/ToDo
@@ -0,0 +1,61 @@
+ToDo: -*- text -*-
+
+----------------------------------------------------------------------
+Memory allocation locality:
+
+Currently mallocs memory in a very haphazard manner. As such, most of
+the program ends up core-resident all the time just to follow all the
+stupid pointers around. . . .
+
+----------------------------------------------------------------------
+Input parser:
+
+The reader implemented in readfile.c could use improvement. Some sort
+of "data-driven" parser should be used so the big switch statements
+would have only one case for each data type instead of one case for
+every recognized option symbol. Then adding a new tag would involve
+only adding a new element to the data table describing known symbols.
+Hopefully, this would shrink the code a bit too. -gwr
+
+----------------------------------------------------------------------
+SLIP Initialization via BOOTP:
+
+In the function handle_request(), both in bootpd and bootpgw,
+we might want to add code like the following just before testing
+the client IP address field for zero. (bp->bp_ciaddr == 0)
+(David suggests we leave this out for now. -gwr)
+
+#if 1 /* XXX - Experimental */
+ /*
+ * SLIP initialization support.
+ *
+ * If this packet came from a SLIP driver that does
+ * automatic IP address initialization, then the socket
+ * will have the IP address and the packet will
+ * have zeros for both the IP and HW addresses.
+ *
+ * Thanks to David P. Maynard <dpm@depend.com>
+ * for explaining how this works. -gwr
+ */
+ if ((bp->bp_ciaddr.s_addr == 0) &&
+ (bp->bp_htype == 0))
+ {
+ /* Pretend the client knows its address. It will soon. */
+ bp->bp_ciaddr = recv_addr.sin_addr;
+ if (debug)
+ report(LOG_INFO, "fixed blank request from IP addr %s",
+ inet_ntoa(recv_addr.sin_addr));
+ }
+#endif
+
+----------------------------------------------------------------------
+DHCP Support:
+
+There is a set of patches from Jeanette Pauline Middelink
+<middelin@calvin.polyware.iaf.nl> to add DHCP support.
+
+Those patches will be integrated into the BOOTP release stream
+very soon, but if you can't wait, you can get them from:
+nimbus.anu.edu.au:/pub/tridge/samba/contributed/DHCP.patch
+
+----------------------------------------------------------------------
diff --git a/libexec/bootpd/bootp.h b/libexec/bootpd/bootp.h
new file mode 100644
index 000000000000..343efb459327
--- /dev/null
+++ b/libexec/bootpd/bootp.h
@@ -0,0 +1,147 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+
+/*
+ * Bootstrap Protocol (BOOTP). RFC951 and RFC1395.
+ *
+ *
+ * This file specifies the "implementation-independent" BOOTP protocol
+ * information which is common to both client and server.
+ *
+ */
+
+#include "bptypes.h" /* for int32, u_int32 */
+
+#define BP_CHADDR_LEN 16
+#define BP_SNAME_LEN 64
+#define BP_FILE_LEN 128
+#define BP_VEND_LEN 64
+#define BP_MINPKTSZ 300 /* to check sizeof(struct bootp) */
+/* Overhead to fit a bootp message into an Ethernet packet. */
+#define BP_MSG_OVERHEAD (14 + 20 + 8) /* Ethernet + IP + UDP headers */
+
+struct bootp {
+ unsigned char bp_op; /* packet opcode type */
+ unsigned char bp_htype; /* hardware addr type */
+ unsigned char bp_hlen; /* hardware addr length */
+ unsigned char bp_hops; /* gateway hops */
+ u_int32 bp_xid; /* transaction ID */
+ unsigned short bp_secs; /* seconds since boot began */
+ unsigned short bp_flags; /* RFC1532 broadcast, etc. */
+ struct in_addr bp_ciaddr; /* client IP address */
+ struct in_addr bp_yiaddr; /* 'your' IP address */
+ struct in_addr bp_siaddr; /* server IP address */
+ struct in_addr bp_giaddr; /* gateway IP address */
+ unsigned char bp_chaddr[BP_CHADDR_LEN]; /* client hardware address */
+ char bp_sname[BP_SNAME_LEN]; /* server host name */
+ char bp_file[BP_FILE_LEN]; /* boot file name */
+ unsigned char bp_vend[BP_VEND_LEN]; /* vendor-specific area */
+ /* note that bp_vend can be longer, extending to end of packet. */
+};
+
+/*
+ * UDP port numbers, server and client.
+ */
+#define IPPORT_BOOTPS 67
+#define IPPORT_BOOTPC 68
+
+#define BOOTREPLY 2
+#define BOOTREQUEST 1
+
+/*
+ * Hardware types from Assigned Numbers RFC.
+ */
+#define HTYPE_ETHERNET 1
+#define HTYPE_EXP_ETHERNET 2
+#define HTYPE_AX25 3
+#define HTYPE_PRONET 4
+#define HTYPE_CHAOS 5
+#define HTYPE_IEEE802 6
+#define HTYPE_ARCNET 7
+
+/*
+ * Vendor magic cookie (v_magic) for CMU
+ */
+#define VM_CMU "CMU"
+
+/*
+ * Vendor magic cookie (v_magic) for RFC1048
+ */
+#define VM_RFC1048 { 99, 130, 83, 99 }
+
+
+
+/*
+ * Tag values used to specify what information is being supplied in
+ * the vendor (options) data area of the packet.
+ */
+/* RFC 1048 */
+#define TAG_END ((unsigned char) 255)
+#define TAG_PAD ((unsigned char) 0)
+#define TAG_SUBNET_MASK ((unsigned char) 1)
+#define TAG_TIME_OFFSET ((unsigned char) 2)
+#define TAG_GATEWAY ((unsigned char) 3)
+#define TAG_TIME_SERVER ((unsigned char) 4)
+#define TAG_NAME_SERVER ((unsigned char) 5)
+#define TAG_DOMAIN_SERVER ((unsigned char) 6)
+#define TAG_LOG_SERVER ((unsigned char) 7)
+#define TAG_COOKIE_SERVER ((unsigned char) 8)
+#define TAG_LPR_SERVER ((unsigned char) 9)
+#define TAG_IMPRESS_SERVER ((unsigned char) 10)
+#define TAG_RLP_SERVER ((unsigned char) 11)
+#define TAG_HOST_NAME ((unsigned char) 12)
+#define TAG_BOOT_SIZE ((unsigned char) 13)
+/* RFC 1395 */
+#define TAG_DUMP_FILE ((unsigned char) 14)
+#define TAG_DOMAIN_NAME ((unsigned char) 15)
+#define TAG_SWAP_SERVER ((unsigned char) 16)
+#define TAG_ROOT_PATH ((unsigned char) 17)
+/* RFC 1497 */
+#define TAG_EXTEN_FILE ((unsigned char) 18)
+/* RFC 1533 */
+#define TAG_NIS_DOMAIN ((unsigned char) 40)
+#define TAG_NIS_SERVER ((unsigned char) 41)
+#define TAG_NTP_SERVER ((unsigned char) 42)
+/* DHCP maximum message size. */
+#define TAG_MAX_MSGSZ ((unsigned char) 57)
+
+/* XXX - Add new tags here */
+
+
+/*
+ * "vendor" data permitted for CMU bootp clients.
+ */
+
+struct cmu_vend {
+ char v_magic[4]; /* magic number */
+ u_int32 v_flags; /* flags/opcodes, etc. */
+ struct in_addr v_smask; /* Subnet mask */
+ struct in_addr v_dgate; /* Default gateway */
+ struct in_addr v_dns1, v_dns2; /* Domain name servers */
+ struct in_addr v_ins1, v_ins2; /* IEN-116 name servers */
+ struct in_addr v_ts1, v_ts2; /* Time servers */
+ int32 v_unused[6]; /* currently unused */
+};
+
+
+/* v_flags values */
+#define VF_SMASK 1 /* Subnet mask field contains valid data */
diff --git a/libexec/bootpd/bootpd.8 b/libexec/bootpd/bootpd.8
new file mode 100644
index 000000000000..e0b780074ac1
--- /dev/null
+++ b/libexec/bootpd/bootpd.8
@@ -0,0 +1,310 @@
+.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University
+.\"
+.Dd May 21, 2019
+.Dt BOOTPD 8
+.Os
+.Sh NAME
+.Nm bootpd , bootpgw
+.Nd Internet Boot Protocol server/gateway
+.Sh SYNOPSIS
+.Nm
+.Op Fl i | s
+.Op Fl c Ar chdir-path
+.Op Fl d Ar level
+.Op Fl h Ar hostname
+.Op Fl t Ar timeout
+.Oo
+.Ar bootptab
+.Op Ar dumpfile
+.Oc
+.Nm bootpgw
+.Op Fl i | s
+.Op Fl d Ar level
+.Op Fl h Ar hostname
+.Op Fl t Ar timeout
+.Ar server
+.Sh DESCRIPTION
+The
+.Nm
+utility
+implements an Internet Bootstrap Protocol (BOOTP) server as defined in
+RFC951, RFC1532, and RFC1533.
+The
+.Nm bootpgw
+utility implements a simple BOOTP gateway which can be used to forward
+requests and responses between clients on one subnet and a
+BOOTP server (i.e.\&
+.Nm )
+on another subnet.
+While either
+.Nm
+or
+.Nm bootpgw
+will forward BOOTREPLY packets, only
+.Nm bootpgw
+will forward BOOTREQUEST packets.
+.Pp
+One host on each network segment is normally configured to run either
+.Nm
+or
+.Nm bootpgw
+from
+.Xr inetd 8
+by including one of the following lines in the file
+.Pa /etc/inetd.conf :
+.Pp
+.Dl bootps dgram udp wait root /usr/libexec/bootpd bootpd /etc/bootptab
+.Dl bootps dgram udp wait root /usr/libexec/bootpgw bootpgw server
+.Pp
+This mode of operation is referred to as "inetd mode" and causes
+.Nm
+(or
+.Nm bootpgw )
+to be started only when a boot request arrives.
+If it does not
+receive another packet within fifteen minutes of the last one
+it received, it will exit to conserve system resources.
+The
+.Fl t
+option controls this timeout (see OPTIONS).
+.Pp
+It is also possible to run
+.Nm
+(or
+.Nm bootpgw )
+in "standalone mode" (without
+.Xr inetd 8 )
+by simply invoking it from a shell like any other regular command.
+Standalone mode is particularly useful when
+.Nm
+is used with a large configuration database, where the start up
+delay might otherwise prevent timely response to client requests.
+(Automatic start up in standalone mode can be done by invoking
+.Nm
+from within
+.Pa /etc/rc.local ,
+for example.)
+Standalone mode is less useful for
+.Nm bootpgw
+which
+has very little start up delay because
+it does not read a configuration file.
+.Pp
+Either program automatically detects whether it was invoked from inetd
+or from a shell and automatically selects the appropriate mode.
+The
+.Fl s
+or
+.Fl i
+option may be used to force standalone or inetd mode respectively
+(see OPTIONS).
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Skip ARP table modifications.
+.It Fl t Ar timeout
+Specify the
+.Ar timeout
+value (in minutes) that a
+.Nm
+or
+.Nm bootpgw
+process will wait for a BOOTP packet before exiting.
+If no packets are received for
+.Ar timeout
+minutes, then the program will exit.
+A timeout value of zero means "run forever".
+In standalone mode, this option is forced to zero.
+.It Fl d Ar debug-level
+Set the
+.Ar debug-level
+variable that controls the amount of debugging messages generated.
+For example,
+.Fl d Ns 4
+or
+.Fl d
+4 will set the debugging level to 4.
+For compatibility with older versions of
+.Nm ,
+omitting the numeric parameter (i.e., just
+.Fl d )
+will simply increment the debug level by one.
+.It Fl c Ar chdir-path
+Set the current directory used by
+.Nm
+while checking the existence and size of client boot files.
+This is
+useful when client boot files are specified as relative pathnames, and
+.Nm
+needs to use the same current directory as the TFTP server
+(typically
+.Pa /tftpboot ) .
+This option is not recognized by
+.Nm bootpgw .
+.It Fl h Ar hostname
+Specify the hostname corresponding to the IP address to listen on.
+By default,
+.Nm
+listens on the IP address corresponding to the machine's hostname, as
+returned by
+.Xr gethostname 3 .
+.It Fl i
+Force inetd mode.
+This option is obsolete, but remains for
+compatibility with older versions of
+.Nm .
+.It Fl s
+Force standalone mode.
+This option is obsolete, but remains for
+compatibility with older versions of
+.Nm .
+.It Ar bootptab
+Specify the name of the configuration file from which
+.Nm
+loads its database of known clients and client options
+.No ( Nm
+only).
+.It Ar dumpfile
+Specify the name of the file that
+.Nm
+will dump its internal database into when it receives a
+SIGUSR1 signal
+.No ( Nm
+only).
+This option is only recognized if
+.Nm
+was compiled with the -DDEBUG flag.
+.It Ar server
+Specify the name of a BOOTP server to which
+.Nm bootpgw
+will forward all BOOTREQUEST packets it receives
+.Pf ( Nm bootpgw
+only).
+.El
+.Sh OPERATION
+Both
+.Nm
+and
+.Nm bootpgw
+operate similarly in that both listen for any packets sent to the
+.Em bootps
+port, and both simply forward any BOOTREPLY packets.
+They differ in their handling of BOOTREQUEST packets.
+.Pp
+When
+.Nm bootpgw
+is started, it determines the address of a BOOTP server
+whose name is provided as a command line parameter.
+When
+.Nm bootpgw
+receives a BOOTREQUEST packet, it sets the "gateway address"
+and "hop count" fields in the packet and forwards the packet
+to the BOOTP server at the address determined earlier.
+Requests are forwarded only if they indicate that
+the client has been waiting for at least three seconds.
+.Pp
+When
+.Nm
+is started it reads a configuration file, (normally
+.Pa /etc/bootptab )
+that initializes the internal database of known clients and client
+options.
+This internal database is reloaded
+from the configuration file when
+.Nm
+receives a hangup signal (SIGHUP) or when it discovers that the
+configuration file has changed.
+.Pp
+When
+.Nm
+receives a BOOTREQUEST packet, it
+.\" checks the modification time of the
+.\" configuration file and reloads the database if necessary. Then it
+looks for a database entry matching the client request.
+If the client is known,
+.Nm
+composes a BOOTREPLY packet using the database entry found above,
+and sends the reply to the client (possibly using a gateway).
+If the client is unknown, the request is discarded
+(with a notice if debug > 0).
+.Pp
+If
+.Nm
+is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes
+it to dump its internal database to the file
+.Pa /tmp/bootpd.dump
+or the dumpfile specified as a command line parameter.
+.Pp
+During initialization, both programs
+determine the UDP port numbers to be used by calling
+.Xr getservbyname 3
+(which normally uses
+.Pa /etc/services ) .
+Two service names (and port numbers) are used:
+.Pp
+.Dl bootps BOOTP Server listening port
+.Dl bootpc BOOTP Client destination port
+.Pp
+If the port numbers cannot be determined using
+.Xr getservbyname 3
+then the values default to bootps=67 and bootpc=68.
+.Sh FILES
+.Bl -tag -width /tmp/bootpd.dump -compact
+.It Pa /etc/bootptab
+Database file read by
+.Nm .
+.It Pa /tmp/bootpd.dump
+Debugging dump file created by
+.Nm .
+.It Pa /etc/services
+Internet service numbers.
+.It Pa /tftpboot
+Current directory typically used by the TFTP server and
+.Nm .
+.El
+.Sh "SEE ALSO"
+.Xr bootptab 5 ,
+.Xr inetd 8 ,
+.Xr tftpd 8
+.Pp
+DARPA Internet Request For Comments:
+.Bl -tag -width RFC1533 -compact
+.It RFC951
+Bootstrap Protocol
+.It RFC1532
+Clarifications and Extensions for the Bootstrap Protocol
+.It RFC1533
+DHCP Options and BOOTP Vendor Extensions
+.El
+.Sh AUTHORS
+This distribution is currently maintained by
+.An Walter L. Wimer Aq Mt walt+@cmu.edu .
+.Pp
+The original BOOTP server was created by
+.An Bill Croft
+at Stanford University in January 1986.
+.Pp
+The current version of
+.Nm
+is primarily the work of
+.An David Kovar ,
+.An Drew D. Perkins ,
+and
+.An Walter L. Wimer ,
+at Carnegie Mellon University.
+.Pp
+Enhancements and bug-fixes have been contributed by:
+.Pp
+(in alphabetical order)
+.Pp
+.An -split
+.An Danny Backx Aq Mt db@sunbim.be
+.An John Brezak Aq Mt brezak@ch.hp.com
+.An Frank da Cruz Aq Mt fdc@cc.columbia.edu
+.An David R. Linn Aq Mt drl@vuse.vanderbilt.edu
+.An Jim McKim Aq Mt mckim@lerc.nasa.gov
+.An Gordon W. Ross Aq Mt gwr@mc.com
+.An Jason Zions Aq Mt jazz@hal.com .
+.Sh BUGS
+Individual host entries must not exceed 1024 characters.
diff --git a/libexec/bootpd/bootpd.c b/libexec/bootpd/bootpd.c
new file mode 100644
index 000000000000..f73671dab074
--- /dev/null
+++ b/libexec/bootpd/bootpd.c
@@ -0,0 +1,1396 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+/*
+ * BOOTP (bootstrap protocol) server daemon.
+ *
+ * Answers BOOTP request packets from booting client machines.
+ * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
+ * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
+ * See RFC 1395 for option tags 14-17.
+ * See accompanying man page -- bootpd.8
+ *
+ * HISTORY
+ * See ./Changes
+ *
+ * BUGS
+ * See ./ToDo
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <paths.h>
+#include <syslog.h>
+#include <assert.h>
+#include <inttypes.h>
+
+#ifdef NO_SETSID
+# include <fcntl.h> /* for O_RDONLY, etc */
+#endif
+
+#include "bootp.h"
+#include "hash.h"
+#include "hwaddr.h"
+#include "bootpd.h"
+#include "dovend.h"
+#include "getif.h"
+#include "readfile.h"
+#include "report.h"
+#include "tzone.h"
+#include "patchlevel.h"
+
+#ifndef CONFIG_FILE
+#define CONFIG_FILE "/etc/bootptab"
+#endif
+#ifndef DUMPTAB_FILE
+#define DUMPTAB_FILE "/tmp/bootpd.dump"
+#endif
+
+
+
+/*
+ * Externals, forward declarations, and global variables
+ */
+
+extern void dumptab(char *);
+
+PRIVATE void catcher(int);
+PRIVATE int chk_access(char *, int32 *);
+#ifdef VEND_CMU
+PRIVATE void dovend_cmu(struct bootp *, struct host *);
+#endif
+PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
+PRIVATE void handle_reply(void);
+PRIVATE void handle_request(void);
+PRIVATE void sendreply(int forward, int32 dest_override);
+PRIVATE void usage(void);
+
+/*
+ * IP port numbers for client and server obtained from /etc/services
+ */
+
+u_short bootps_port, bootpc_port;
+
+
+/*
+ * Internet socket and interface config structures
+ */
+
+struct sockaddr_in bind_addr; /* Listening */
+struct sockaddr_in recv_addr; /* Packet source */
+struct sockaddr_in send_addr; /* destination */
+
+
+/*
+ * option defaults
+ */
+int debug = 0; /* Debugging flag (level) */
+struct timeval actualtimeout =
+{ /* fifteen minutes */
+ 15 * 60L, /* tv_sec */
+ 0 /* tv_usec */
+};
+int arpmod = TRUE; /* modify the ARP table */
+
+/*
+ * General
+ */
+
+int s; /* Socket file descriptor */
+char *pktbuf; /* Receive packet buffer */
+int pktlen;
+char *progname;
+char *chdir_path;
+struct in_addr my_ip_addr;
+
+static const char *hostname;
+static char default_hostname[MAXHOSTNAMELEN];
+
+/* Flags set by signal catcher. */
+PRIVATE int do_readtab = 0;
+PRIVATE int do_dumptab = 0;
+
+/*
+ * Globals below are associated with the bootp database file (bootptab).
+ */
+
+char *bootptab = CONFIG_FILE;
+char *bootpd_dump = DUMPTAB_FILE;
+
+
+
+/*
+ * Initialization such as command-line processing is done and then the
+ * main server loop is started.
+ */
+
+int
+main(int argc, char **argv)
+{
+ struct timeval *timeout;
+ struct bootp *bp;
+ struct servent *servp;
+ struct hostent *hep;
+ char *stmp;
+ socklen_t ba_len, ra_len;
+ int n;
+ int nfound;
+ fd_set readfds;
+ int standalone;
+#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */
+ struct sigaction sa;
+#endif
+
+ progname = strrchr(argv[0], '/');
+ if (progname) progname++;
+ else progname = argv[0];
+
+ /*
+ * Initialize logging.
+ */
+ report_init(0); /* uses progname */
+
+ /*
+ * Log startup
+ */
+ report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
+
+ /* Debugging for compilers with struct padding. */
+ assert(sizeof(struct bootp) == BP_MINPKTSZ);
+
+ /* Get space for receiving packets and composing replies. */
+ pktbuf = malloc(MAX_MSG_SIZE);
+ if (!pktbuf) {
+ report(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+ bp = (struct bootp *) pktbuf;
+
+ /*
+ * Check to see if a socket was passed to us from inetd.
+ *
+ * Use getsockname() to determine if descriptor 0 is indeed a socket
+ * (and thus we are probably a child of inetd) or if it is instead
+ * something else and we are running standalone.
+ */
+ s = 0;
+ ba_len = sizeof(bind_addr);
+ bzero((char *) &bind_addr, ba_len);
+ errno = 0;
+ standalone = TRUE;
+ if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
+ /*
+ * Descriptor 0 is a socket. Assume we are a child of inetd.
+ */
+ if (bind_addr.sin_family == AF_INET) {
+ standalone = FALSE;
+ bootps_port = ntohs(bind_addr.sin_port);
+ } else {
+ /* Some other type of socket? */
+ report(LOG_ERR, "getsockname: not an INET socket");
+ }
+ }
+
+ /*
+ * Set defaults that might be changed by option switches.
+ */
+ stmp = NULL;
+ timeout = &actualtimeout;
+
+ if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
+ report(LOG_ERR, "bootpd: can't get hostname\n");
+ exit(1);
+ }
+ default_hostname[sizeof(default_hostname) - 1] = '\0';
+ hostname = default_hostname;
+
+ /*
+ * Read switches.
+ */
+ for (argc--, argv++; argc > 0; argc--, argv++) {
+ if (argv[0][0] != '-')
+ break;
+ switch (argv[0][1]) {
+
+ case 'a': /* don't modify the ARP table */
+ arpmod = FALSE;
+ break;
+ case 'c': /* chdir_path */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (stmp[0] != '/')) {
+ report(LOG_ERR,
+ "bootpd: invalid chdir specification\n");
+ break;
+ }
+ chdir_path = stmp;
+ break;
+
+ case 'd': /* debug level */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else if (argv[1] && argv[1][0] == '-') {
+ /*
+ * Backwards-compatible behavior:
+ * no parameter, so just increment the debug flag.
+ */
+ debug++;
+ break;
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ report(LOG_ERR,
+ "%s: invalid debug level\n", progname);
+ break;
+ }
+ debug = n;
+ break;
+
+ case 'h': /* override hostname */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp) {
+ report(LOG_ERR,
+ "bootpd: missing hostname\n");
+ break;
+ }
+ hostname = stmp;
+ break;
+
+ case 'i': /* inetd mode */
+ standalone = FALSE;
+ break;
+
+ case 's': /* standalone mode */
+ standalone = TRUE;
+ break;
+
+ case 't': /* timeout */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ report(LOG_ERR,
+ "%s: invalid timeout specification\n", progname);
+ break;
+ }
+ actualtimeout.tv_sec = (int32) (60 * n);
+ /*
+ * If the actual timeout is zero, pass a NULL pointer
+ * to select so it blocks indefinitely, otherwise,
+ * point to the actual timeout value.
+ */
+ timeout = (n > 0) ? &actualtimeout : NULL;
+ break;
+
+ default:
+ report(LOG_ERR, "%s: unknown switch: -%c\n",
+ progname, argv[0][1]);
+ usage();
+ break;
+
+ } /* switch */
+ } /* for args */
+
+ /*
+ * Override default file names if specified on the command line.
+ */
+ if (argc > 0)
+ bootptab = argv[0];
+
+ if (argc > 1)
+ bootpd_dump = argv[1];
+
+ /*
+ * Get my hostname and IP address.
+ */
+
+ hep = gethostbyname(hostname);
+ if (!hep) {
+ report(LOG_ERR, "Can not get my IP address\n");
+ exit(1);
+ }
+ bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
+
+ if (standalone) {
+ /*
+ * Go into background and disassociate from controlling terminal.
+ */
+ if (debug < 3) {
+ if (fork())
+ exit(0);
+#ifdef NO_SETSID
+ setpgrp(0,0);
+#ifdef TIOCNOTTY
+ n = open(_PATH_TTY, O_RDWR);
+ if (n >= 0) {
+ ioctl(n, TIOCNOTTY, (char *) 0);
+ (void) close(n);
+ }
+#endif /* TIOCNOTTY */
+#else /* SETSID */
+ if (setsid() < 0)
+ perror("setsid");
+#endif /* SETSID */
+ } /* if debug < 3 */
+
+ /*
+ * Nuke any timeout value
+ */
+ timeout = NULL;
+
+ } /* if standalone (1st) */
+
+ /* Set the cwd (i.e. to /tftpboot) */
+ if (chdir_path) {
+ if (chdir(chdir_path) < 0)
+ report(LOG_ERR, "%s: chdir failed", chdir_path);
+ }
+
+ /* Get the timezone. */
+ tzone_init();
+
+ /* Allocate hash tables. */
+ rdtab_init();
+
+ /*
+ * Read the bootptab file.
+ */
+ readtab(1); /* force read */
+
+ if (standalone) {
+
+ /*
+ * Create a socket.
+ */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ report(LOG_ERR, "socket: %s", get_network_errmsg());
+ exit(1);
+ }
+
+ /*
+ * Get server's listening port number
+ */
+ servp = getservbyname("bootps", "udp");
+ if (servp) {
+ bootps_port = ntohs((u_short) servp->s_port);
+ } else {
+ bootps_port = (u_short) IPPORT_BOOTPS;
+ report(LOG_ERR,
+ "bootps/udp: unknown service -- using port %d",
+ bootps_port);
+ }
+
+ /*
+ * Bind socket to BOOTPS port.
+ */
+ bind_addr.sin_family = AF_INET;
+ bind_addr.sin_addr.s_addr = INADDR_ANY;
+ bind_addr.sin_port = htons(bootps_port);
+ if (bind(s, (struct sockaddr *) &bind_addr,
+ sizeof(bind_addr)) < 0)
+ {
+ report(LOG_ERR, "bind: %s", get_network_errmsg());
+ exit(1);
+ }
+ } /* if standalone (2nd)*/
+
+ /*
+ * Get destination port number so we can reply to client
+ */
+ servp = getservbyname("bootpc", "udp");
+ if (servp) {
+ bootpc_port = ntohs(servp->s_port);
+ } else {
+ report(LOG_ERR,
+ "bootpc/udp: unknown service -- using port %d",
+ IPPORT_BOOTPC);
+ bootpc_port = (u_short) IPPORT_BOOTPC;
+ }
+
+ /*
+ * Set up signals to read or dump the table.
+ */
+#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */
+ sa.sa_handler = catcher;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ if (sigaction(SIGHUP, &sa, NULL) < 0) {
+ report(LOG_ERR, "sigaction: %s", get_errmsg());
+ exit(1);
+ }
+ if (sigaction(SIGUSR1, &sa, NULL) < 0) {
+ report(LOG_ERR, "sigaction: %s", get_errmsg());
+ exit(1);
+ }
+#else /* SA_NOCLDSTOP */
+ /* Old-fashioned UNIX signals */
+ if ((int) signal(SIGHUP, catcher) < 0) {
+ report(LOG_ERR, "signal: %s", get_errmsg());
+ exit(1);
+ }
+ if ((int) signal(SIGUSR1, catcher) < 0) {
+ report(LOG_ERR, "signal: %s", get_errmsg());
+ exit(1);
+ }
+#endif /* SA_NOCLDSTOP */
+
+ /*
+ * Process incoming requests.
+ */
+ FD_ZERO(&readfds);
+ for (;;) {
+ struct timeval tv;
+
+ FD_SET(s, &readfds);
+ if (timeout)
+ tv = *timeout;
+
+ nfound = select(s + 1, &readfds, NULL, NULL,
+ (timeout) ? &tv : NULL);
+ if (nfound < 0) {
+ if (errno != EINTR) {
+ report(LOG_ERR, "select: %s", get_errmsg());
+ }
+ /*
+ * Call readtab() or dumptab() here to avoid the
+ * dangers of doing I/O from a signal handler.
+ */
+ if (do_readtab) {
+ do_readtab = 0;
+ readtab(1); /* force read */
+ }
+ if (do_dumptab) {
+ do_dumptab = 0;
+ dumptab(bootpd_dump);
+ }
+ continue;
+ }
+ if (!FD_ISSET(s, &readfds)) {
+ if (debug > 1)
+ report(LOG_INFO, "exiting after %jd minutes of inactivity",
+ (intmax_t)actualtimeout.tv_sec / 60);
+ exit(0);
+ }
+ ra_len = sizeof(recv_addr);
+ n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
+ (struct sockaddr *) &recv_addr, &ra_len);
+ if (n <= 0) {
+ continue;
+ }
+ if (debug > 1) {
+ report(LOG_INFO, "recvd pkt from IP addr %s",
+ inet_ntoa(recv_addr.sin_addr));
+ }
+ if (n < sizeof(struct bootp)) {
+ if (debug) {
+ report(LOG_NOTICE, "received short packet");
+ }
+ continue;
+ }
+ pktlen = n;
+
+ readtab(0); /* maybe re-read bootptab */
+
+ switch (bp->bp_op) {
+ case BOOTREQUEST:
+ handle_request();
+ break;
+ case BOOTREPLY:
+ handle_reply();
+ break;
+ }
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * Print "usage" message and exit
+ */
+
+PRIVATE void
+usage()
+{
+ fprintf(stderr,
+ "usage: bootpd [-a] [-i | -s] [-c chdir-path] [-d level] [-h hostname]\n"
+ " [-t timeout] [bootptab [dumpfile]]\n");
+ fprintf(stderr, " -a\tdon't modify ARP table\n");
+ fprintf(stderr, " -c n\tset current directory\n");
+ fprintf(stderr, " -d n\tset debug level\n");
+ fprintf(stderr, " -h n\tset the hostname to listen on\n");
+ fprintf(stderr, " -i\tforce inetd mode (run as child of inetd)\n");
+ fprintf(stderr, " -s\tforce standalone mode (run without inetd)\n");
+ fprintf(stderr, " -t n\tset inetd exit timeout to n minutes\n");
+ exit(1);
+}
+
+/* Signal catchers */
+PRIVATE void
+catcher(int sig)
+{
+ if (sig == SIGHUP)
+ do_readtab = 1;
+ if (sig == SIGUSR1)
+ do_dumptab = 1;
+#if !defined(SA_NOCLDSTOP) && defined(SYSV)
+ /* For older "System V" derivatives with no sigaction(). */
+ signal(sig, catcher);
+#endif
+}
+
+
+
+/*
+ * Process BOOTREQUEST packet.
+ *
+ * Note: This version of the bootpd.c server never forwards
+ * a request to another server. That is the job of a gateway
+ * program such as the "bootpgw" program included here.
+ *
+ * (Also this version does not interpret the hostname field of
+ * the request packet; it COULD do a name->address lookup and
+ * forward the request there.)
+ */
+PRIVATE void
+handle_request(void)
+{
+ struct bootp *bp = (struct bootp *) pktbuf;
+ struct host *hp = NULL;
+ struct host dummyhost;
+ int32 bootsize = 0;
+ unsigned hlen, hashcode;
+ int32 dest;
+ char realpath[1024];
+ char *clntpath;
+ char *homedir, *bootfile;
+ int n;
+
+ if (bp->bp_htype >= hwinfocnt) {
+ report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype);
+ return;
+ }
+ bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
+
+ /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
+
+ /*
+ * If the servername field is set, compare it against us.
+ * If we're not being addressed, ignore this request.
+ * If the server name field is null, throw in our name.
+ */
+ if (strlen(bp->bp_sname)) {
+ if (strcmp(bp->bp_sname, hostname)) {
+ if (debug)
+ report(LOG_INFO, "\
+ignoring request for server %s from client at %s address %s",
+ bp->bp_sname, netname(bp->bp_htype),
+ haddrtoa(bp->bp_chaddr, bp->bp_hlen));
+ /* XXX - Is it correct to ignore such a request? -gwr */
+ return;
+ }
+ } else {
+ strcpy(bp->bp_sname, hostname);
+ }
+
+ /* Convert the request into a reply. */
+ bp->bp_op = BOOTREPLY;
+ if (bp->bp_ciaddr.s_addr == 0) {
+ /*
+ * client doesn't know his IP address,
+ * search by hardware address.
+ */
+ if (debug > 1) {
+ report(LOG_INFO, "request from %s address %s",
+ netname(bp->bp_htype),
+ haddrtoa(bp->bp_chaddr, bp->bp_hlen));
+ }
+ hlen = haddrlength(bp->bp_htype);
+ if (hlen != bp->bp_hlen) {
+ report(LOG_NOTICE, "bad addr len from %s address %s",
+ netname(bp->bp_htype),
+ haddrtoa(bp->bp_chaddr, hlen));
+ }
+ dummyhost.htype = bp->bp_htype;
+ bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
+ hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
+ hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
+ &dummyhost);
+ if (hp == NULL &&
+ bp->bp_htype == HTYPE_IEEE802)
+ {
+ /* Try again with address in "canonical" form. */
+ haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
+ if (debug > 1) {
+ report(LOG_INFO, "\
+HW addr type is IEEE 802. convert to %s and check again\n",
+ haddrtoa(dummyhost.haddr, bp->bp_hlen));
+ }
+ hashcode = hash_HashFunction(dummyhost.haddr, hlen);
+ hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
+ hwlookcmp, &dummyhost);
+ }
+ if (hp == NULL) {
+ /*
+ * XXX - Add dynamic IP address assignment?
+ */
+ if (debug)
+ report(LOG_NOTICE, "unknown client %s address %s",
+ netname(bp->bp_htype),
+ haddrtoa(bp->bp_chaddr, bp->bp_hlen));
+ return; /* not found */
+ }
+ (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
+
+ } else {
+
+ /*
+ * search by IP address.
+ */
+ if (debug > 1) {
+ report(LOG_INFO, "request from IP addr %s",
+ inet_ntoa(bp->bp_ciaddr));
+ }
+ dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
+ hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
+ hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
+ &dummyhost);
+ if (hp == NULL) {
+ if (debug) {
+ report(LOG_NOTICE, "IP address not found: %s",
+ inet_ntoa(bp->bp_ciaddr));
+ }
+ return;
+ }
+ }
+
+ if (debug) {
+ report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
+ hp->hostname->string);
+ }
+
+ /*
+ * If there is a response delay threshold, ignore requests
+ * with a timestamp lower than the threshold.
+ */
+ if (hp->flags.min_wait) {
+ u_int32 t = (u_int32) ntohs(bp->bp_secs);
+ if (t < hp->min_wait) {
+ if (debug > 1)
+ report(LOG_INFO,
+ "ignoring request due to timestamp (%d < %d)",
+ t, hp->min_wait);
+ return;
+ }
+ }
+
+#ifdef YORK_EX_OPTION
+ /*
+ * The need for the "ex" tag arose out of the need to empty
+ * shared networked drives on diskless PCs. This solution is
+ * not very clean but it does work fairly well.
+ * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
+ *
+ * XXX - This could compromise security if a non-trusted user
+ * managed to write an entry in the bootptab with :ex=trojan:
+ * so I would leave this turned off unless you need it. -gwr
+ */
+ /* Run a program, passing the client name as a parameter. */
+ if (hp->flags.exec_file) {
+ char tst[100];
+ /* XXX - Check string lengths? -gwr */
+ strcpy (tst, hp->exec_file->string);
+ strcat (tst, " ");
+ strcat (tst, hp->hostname->string);
+ strcat (tst, " &");
+ if (debug)
+ report(LOG_INFO, "executing %s", tst);
+ system(tst); /* Hope this finishes soon... */
+ }
+#endif /* YORK_EX_OPTION */
+
+ /*
+ * If a specific TFTP server address was specified in the bootptab file,
+ * fill it in, otherwise zero it.
+ * XXX - Rather than zero it, should it be the bootpd address? -gwr
+ */
+ (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
+ hp->bootserver.s_addr : 0L;
+
+#ifdef STANFORD_PROM_COMPAT
+ /*
+ * Stanford bootp PROMs (for a Sun?) have no way to leave
+ * the boot file name field blank (because the boot file
+ * name is automatically generated from some index).
+ * As a work-around, this little hack allows those PROMs to
+ * specify "sunboot14" with the same effect as a NULL name.
+ * (The user specifies boot device 14 or some such magic.)
+ */
+ if (strcmp(bp->bp_file, "sunboot14") == 0)
+ bp->bp_file[0] = '\0'; /* treat it as unspecified */
+#endif
+
+ /*
+ * Fill in the client's proper bootfile.
+ *
+ * If the client specifies an absolute path, try that file with a
+ * ".host" suffix and then without. If the file cannot be found, no
+ * reply is made at all.
+ *
+ * If the client specifies a null or relative file, use the following
+ * table to determine the appropriate action:
+ *
+ * Homedir Bootfile Client's file
+ * specified? specified? specification Action
+ * -------------------------------------------------------------------
+ * No No Null Send null filename
+ * No No Relative Discard request
+ * No Yes Null Send if absolute else null
+ * No Yes Relative Discard request *XXX
+ * Yes No Null Send null filename
+ * Yes No Relative Lookup with ".host"
+ * Yes Yes Null Send home/boot or bootfile
+ * Yes Yes Relative Lookup with ".host" *XXX
+ *
+ */
+
+ /*
+ * XXX - I don't like the policy of ignoring a client when the
+ * boot file is not accessible. The TFTP server might not be
+ * running on the same machine as the BOOTP server, in which
+ * case checking accessibility of the boot file is pointless.
+ *
+ * Therefore, file accessibility is now demanded ONLY if you
+ * define CHECK_FILE_ACCESS in the Makefile options. -gwr
+ */
+
+ /*
+ * The "real" path is as seen by the BOOTP daemon on this
+ * machine, while the client path is relative to the TFTP
+ * daemon chroot directory (i.e. /tftpboot).
+ */
+ if (hp->flags.tftpdir) {
+ snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
+ clntpath = &realpath[strlen(realpath)];
+ } else {
+ realpath[0] = '\0';
+ clntpath = realpath;
+ }
+
+ /*
+ * Determine client's requested homedir and bootfile.
+ */
+ homedir = NULL;
+ bootfile = NULL;
+ if (bp->bp_file[0]) {
+ homedir = bp->bp_file;
+ bootfile = strrchr(homedir, '/');
+ if (bootfile) {
+ if (homedir == bootfile)
+ homedir = NULL;
+ *bootfile++ = '\0';
+ } else {
+ /* no "/" in the string */
+ bootfile = homedir;
+ homedir = NULL;
+ }
+ if (debug > 2) {
+ report(LOG_INFO, "requested path=\"%s\" file=\"%s\"",
+ (homedir) ? homedir : "",
+ (bootfile) ? bootfile : "");
+ }
+ }
+
+ /*
+ * Specifications in bootptab override client requested values.
+ */
+ if (hp->flags.homedir)
+ homedir = hp->homedir->string;
+ if (hp->flags.bootfile)
+ bootfile = hp->bootfile->string;
+
+ /*
+ * Construct bootfile path.
+ */
+ if (homedir) {
+ if (homedir[0] != '/')
+ strcat(clntpath, "/");
+ strcat(clntpath, homedir);
+ homedir = NULL;
+ }
+ if (bootfile) {
+ if (bootfile[0] != '/')
+ strcat(clntpath, "/");
+ strcat(clntpath, bootfile);
+ bootfile = NULL;
+ }
+
+ /*
+ * First try to find the file with a ".host" suffix
+ */
+ n = strlen(clntpath);
+ strcat(clntpath, ".");
+ strcat(clntpath, hp->hostname->string);
+ if (chk_access(realpath, &bootsize) < 0) {
+ clntpath[n] = 0; /* Try it without the suffix */
+ if (chk_access(realpath, &bootsize) < 0) {
+ /* neither "file.host" nor "file" was found */
+#ifdef CHECK_FILE_ACCESS
+
+ if (bp->bp_file[0]) {
+ /*
+ * Client wanted specific file
+ * and we didn't have it.
+ */
+ report(LOG_NOTICE,
+ "requested file not found: \"%s\"", clntpath);
+ return;
+ }
+ /*
+ * Client didn't ask for a specific file and we couldn't
+ * access the default file, so just zero-out the bootfile
+ * field in the packet and continue processing the reply.
+ */
+ bzero(bp->bp_file, sizeof(bp->bp_file));
+ goto null_file_name;
+
+#else /* CHECK_FILE_ACCESS */
+
+ /* Complain only if boot file size was needed. */
+ if (hp->flags.bootsize_auto) {
+ report(LOG_ERR, "can not determine size of file \"%s\"",
+ clntpath);
+ }
+
+#endif /* CHECK_FILE_ACCESS */
+ }
+ }
+ strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
+ if (debug > 2)
+ report(LOG_INFO, "bootfile=\"%s\"", clntpath);
+
+#ifdef CHECK_FILE_ACCESS
+null_file_name:
+#endif /* CHECK_FILE_ACCESS */
+
+
+ /*
+ * Handle vendor options based on magic number.
+ */
+
+ if (debug > 1) {
+ report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
+ (int) ((bp->bp_vend)[0]),
+ (int) ((bp->bp_vend)[1]),
+ (int) ((bp->bp_vend)[2]),
+ (int) ((bp->bp_vend)[3]));
+ }
+ /*
+ * If this host isn't set for automatic vendor info then copy the
+ * specific cookie into the bootp packet, thus forcing a certain
+ * reply format. Only force reply format if user specified it.
+ */
+ if (hp->flags.vm_cookie) {
+ /* Slam in the user specified magic number. */
+ bcopy(hp->vm_cookie, bp->bp_vend, 4);
+ }
+ /*
+ * Figure out the format for the vendor-specific info.
+ * Note that bp->bp_vend may have been set above.
+ */
+ if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
+ /* RFC1048 conformant bootp client */
+ dovend_rfc1048(bp, hp, bootsize);
+ if (debug > 1) {
+ report(LOG_INFO, "sending reply (with RFC1048 options)");
+ }
+ }
+#ifdef VEND_CMU
+ else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
+ dovend_cmu(bp, hp);
+ if (debug > 1) {
+ report(LOG_INFO, "sending reply (with CMU options)");
+ }
+ }
+#endif
+ else {
+ if (debug > 1) {
+ report(LOG_INFO, "sending reply (with no options)");
+ }
+ }
+
+ dest = (hp->flags.reply_addr) ?
+ hp->reply_addr.s_addr : 0L;
+
+ /* not forwarded */
+ sendreply(0, dest);
+}
+
+
+/*
+ * Process BOOTREPLY packet.
+ */
+PRIVATE void
+handle_reply(void)
+{
+ if (debug) {
+ report(LOG_INFO, "processing boot reply");
+ }
+ /* forwarded, no destination override */
+ sendreply(1, 0);
+}
+
+
+/*
+ * Send a reply packet to the client. 'forward' flag is set if we are
+ * not the originator of this reply packet.
+ */
+PRIVATE void
+sendreply(int forward, int32 dst_override)
+{
+ struct bootp *bp = (struct bootp *) pktbuf;
+ struct in_addr dst;
+ u_short port = bootpc_port;
+ unsigned char *ha;
+ int len, haf;
+
+ /*
+ * XXX - Should honor bp_flags "broadcast" bit here.
+ * Temporary workaround: use the :ra=ADDR: option to
+ * set the reply address to the broadcast address.
+ */
+
+ /*
+ * If the destination address was specified explicitly
+ * (i.e. the broadcast address for HP compatibility)
+ * then send the response to that address. Otherwise,
+ * act in accordance with RFC951:
+ * If the client IP address is specified, use that
+ * else if gateway IP address is specified, use that
+ * else make a temporary arp cache entry for the client's
+ * NEW IP/hardware address and use that.
+ */
+ if (dst_override) {
+ dst.s_addr = dst_override;
+ if (debug > 1) {
+ report(LOG_INFO, "reply address override: %s",
+ inet_ntoa(dst));
+ }
+ } else if (bp->bp_ciaddr.s_addr) {
+ dst = bp->bp_ciaddr;
+ } else if (bp->bp_giaddr.s_addr && forward == 0) {
+ dst = bp->bp_giaddr;
+ port = bootps_port;
+ if (debug > 1) {
+ report(LOG_INFO, "sending reply to gateway %s",
+ inet_ntoa(dst));
+ }
+ } else {
+ dst = bp->bp_yiaddr;
+ ha = bp->bp_chaddr;
+ len = bp->bp_hlen;
+ if (len > MAXHADDRLEN)
+ len = MAXHADDRLEN;
+ haf = (int) bp->bp_htype;
+ if (haf == 0)
+ haf = HTYPE_ETHERNET;
+
+ if (arpmod) {
+ if (debug > 1)
+ report(LOG_INFO, "setarp %s - %s",
+ inet_ntoa(dst), haddrtoa(ha, len));
+ setarp(s, &dst, haf, ha, len);
+ }
+ }
+
+ if ((forward == 0) &&
+ (bp->bp_siaddr.s_addr == 0))
+ {
+ struct ifreq *ifr;
+ struct in_addr siaddr;
+ /*
+ * If we are originating this reply, we
+ * need to find our own interface address to
+ * put in the bp_siaddr field of the reply.
+ * If this server is multi-homed, pick the
+ * 'best' interface (the one on the same net
+ * as the client). Of course, the client may
+ * be on the other side of a BOOTP gateway...
+ */
+ ifr = getif(s, &dst);
+ if (ifr) {
+ struct sockaddr_in *sip;
+ sip = (struct sockaddr_in *) &(ifr->ifr_addr);
+ siaddr = sip->sin_addr;
+ } else {
+ /* Just use my "official" IP address. */
+ siaddr = my_ip_addr;
+ }
+
+ /* XXX - No need to set bp_giaddr here. */
+
+ /* Finally, set the server address field. */
+ bp->bp_siaddr = siaddr;
+ }
+ /* Set up socket address for send. */
+ send_addr.sin_family = AF_INET;
+ send_addr.sin_port = htons(port);
+ send_addr.sin_addr = dst;
+
+ /* Send reply with same size packet as request used. */
+ if (sendto(s, pktbuf, pktlen, 0,
+ (struct sockaddr *) &send_addr,
+ sizeof(send_addr)) < 0)
+ {
+ report(LOG_ERR, "sendto: %s", get_network_errmsg());
+ }
+} /* sendreply */
+
+
+/* nmatch() - now in getif.c */
+/* setarp() - now in hwaddr.c */
+
+
+/*
+ * This call checks read access to a file. It returns 0 if the file given
+ * by "path" exists and is publicly readable. A value of -1 is returned if
+ * access is not permitted or an error occurs. Successful calls also
+ * return the file size in bytes using the long pointer "filesize".
+ *
+ * The read permission bit for "other" users is checked. This bit must be
+ * set for tftpd(8) to allow clients to read the file.
+ */
+
+PRIVATE int
+chk_access(char *path, int32 *filesize)
+{
+ struct stat st;
+
+ if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
+ *filesize = (int32) st.st_size;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+
+/*
+ * Now in dumptab.c :
+ * dumptab()
+ * dump_host()
+ * list_ipaddresses()
+ */
+
+#ifdef VEND_CMU
+
+/*
+ * Insert the CMU "vendor" data for the host pointed to by "hp" into the
+ * bootp packet pointed to by "bp".
+ */
+
+PRIVATE void
+dovend_cmu(struct bootp *bp, struct host *hp)
+{
+ struct cmu_vend *vendp;
+ struct in_addr_list *taddr;
+
+ /*
+ * Initialize the entire vendor field to zeroes.
+ */
+ bzero(bp->bp_vend, sizeof(bp->bp_vend));
+
+ /*
+ * Fill in vendor information. Subnet mask, default gateway,
+ * domain name server, ien name server, time server
+ */
+ vendp = (struct cmu_vend *) bp->bp_vend;
+ strcpy(vendp->v_magic, (char *)vm_cmu);
+ if (hp->flags.subnet_mask) {
+ (vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
+ (vendp->v_flags) |= VF_SMASK;
+ if (hp->flags.gateway) {
+ (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
+ }
+ }
+ if (hp->flags.domain_server) {
+ taddr = hp->domain_server;
+ if (taddr->addrcount > 0) {
+ (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
+ if (taddr->addrcount > 1) {
+ (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
+ }
+ }
+ }
+ if (hp->flags.name_server) {
+ taddr = hp->name_server;
+ if (taddr->addrcount > 0) {
+ (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
+ if (taddr->addrcount > 1) {
+ (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
+ }
+ }
+ }
+ if (hp->flags.time_server) {
+ taddr = hp->time_server;
+ if (taddr->addrcount > 0) {
+ (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
+ if (taddr->addrcount > 1) {
+ (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
+ }
+ }
+ }
+ /* Log message now done by caller. */
+} /* dovend_cmu */
+
+#endif /* VEND_CMU */
+
+
+
+/*
+ * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
+ * bootp packet pointed to by "bp".
+ */
+#define NEED(LEN, MSG) do \
+ if (bytesleft < (LEN)) { \
+ report(LOG_NOTICE, noroom, \
+ hp->hostname->string, MSG); \
+ return; \
+ } while (0)
+PRIVATE void
+dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
+{
+ int bytesleft, len;
+ byte *vp;
+
+ static const char noroom[] = "%s: No room for \"%s\" option";
+
+ vp = bp->bp_vend;
+
+ if (hp->flags.msg_size) {
+ pktlen = hp->msg_size;
+ } else {
+ /*
+ * If the request was longer than the official length, build
+ * a response of that same length where the additional length
+ * is assumed to be part of the bp_vend (options) area.
+ */
+ if (pktlen > sizeof(*bp)) {
+ if (debug > 1)
+ report(LOG_INFO, "request message length=%d", pktlen);
+ }
+ /*
+ * Check whether the request contains the option:
+ * Maximum DHCP Message Size (RFC1533 sec. 9.8)
+ * and if so, override the response length with its value.
+ * This request must lie within the first BP_VEND_LEN
+ * bytes of the option space.
+ */
+ {
+ byte *p, *ep;
+ byte tag, len;
+ short msgsz = 0;
+
+ p = vp + 4;
+ ep = p + BP_VEND_LEN - 4;
+ while (p < ep) {
+ tag = *p++;
+ /* Check for tags with no data first. */
+ if (tag == TAG_PAD)
+ continue;
+ if (tag == TAG_END)
+ break;
+ /* Now scan the length byte. */
+ len = *p++;
+ switch (tag) {
+ case TAG_MAX_MSGSZ:
+ if (len == 2) {
+ bcopy(p, (char*)&msgsz, 2);
+ msgsz = ntohs(msgsz);
+ }
+ break;
+ case TAG_SUBNET_MASK:
+ /* XXX - Should preserve this if given... */
+ break;
+ } /* swtich */
+ p += len;
+ }
+
+ if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {
+ if (debug > 1)
+ report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
+ pktlen = msgsz - BP_MSG_OVERHEAD;
+ }
+ }
+ }
+
+ if (pktlen < sizeof(*bp)) {
+ report(LOG_ERR, "invalid response length=%d", pktlen);
+ pktlen = sizeof(*bp);
+ }
+ bytesleft = ((byte*)bp + pktlen) - vp;
+ if (pktlen > sizeof(*bp)) {
+ if (debug > 1)
+ report(LOG_INFO, "extended reply, length=%d, options=%d",
+ pktlen, bytesleft);
+ }
+
+ /* Copy in the magic cookie */
+ bcopy(vm_rfc1048, vp, 4);
+ vp += 4;
+ bytesleft -= 4;
+
+ if (hp->flags.subnet_mask) {
+ /* always enough room here. */
+ *vp++ = TAG_SUBNET_MASK;/* -1 byte */
+ *vp++ = 4; /* -1 byte */
+ insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */
+ bytesleft -= 6; /* Fix real count */
+ if (hp->flags.gateway) {
+ (void) insert_ip(TAG_GATEWAY,
+ hp->gateway,
+ &vp, &bytesleft);
+ }
+ }
+ if (hp->flags.bootsize) {
+ /* always enough room here */
+ bootsize = (hp->flags.bootsize_auto) ?
+ ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */
+ *vp++ = TAG_BOOT_SIZE;
+ *vp++ = 2;
+ *vp++ = (byte) ((bootsize >> 8) & 0xFF);
+ *vp++ = (byte) (bootsize & 0xFF);
+ bytesleft -= 4; /* Tag, length, and 16 bit blocksize */
+ }
+ /*
+ * This one is special: Remaining options go in the ext file.
+ * Only the subnet_mask, bootsize, and gateway should precede.
+ */
+ if (hp->flags.exten_file) {
+ /*
+ * Check for room for exten_file. Add 3 to account for
+ * TAG_EXTEN_FILE, length, and TAG_END.
+ */
+ len = strlen(hp->exten_file->string);
+ NEED((len + 3), "ef");
+ *vp++ = TAG_EXTEN_FILE;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->exten_file->string, vp, len);
+ vp += len;
+ *vp++ = TAG_END;
+ bytesleft -= len + 3;
+ return; /* no more options here. */
+ }
+ /*
+ * The remaining options are inserted by the following
+ * function (which is shared with bootpef.c).
+ * Keep back one byte for the TAG_END.
+ */
+ len = dovend_rfc1497(hp, vp, bytesleft - 1);
+ vp += len;
+ bytesleft -= len;
+
+ /* There should be at least one byte left. */
+ NEED(1, "(end)");
+ *vp++ = TAG_END;
+ bytesleft--;
+
+ /* Log message done by caller. */
+ if (bytesleft > 0) {
+ /*
+ * Zero out any remaining part of the vendor area.
+ */
+ bzero(vp, bytesleft);
+ }
+} /* dovend_rfc1048 */
+#undef NEED
+
+
+/*
+ * Now in readfile.c:
+ * hwlookcmp()
+ * iplookcmp()
+ */
+
+/* haddrtoa() - now in hwaddr.c */
+/*
+ * Now in dovend.c:
+ * insert_ip()
+ * insert_generic()
+ * insert_u_long()
+ */
+
+/* get_errmsg() - now in report.c */
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/bootpd.h b/libexec/bootpd/bootpd.h
new file mode 100644
index 000000000000..11c4a8b41d64
--- /dev/null
+++ b/libexec/bootpd/bootpd.h
@@ -0,0 +1,211 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+
+
+/*
+ * bootpd.h -- common header file for all the modules of the bootpd program.
+ */
+
+#include "bptypes.h"
+#include "hash.h"
+#include "hwaddr.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef PRIVATE
+#define PRIVATE static
+#endif
+
+#ifndef SIGUSR1
+#define SIGUSR1 30 /* From 4.3 <signal.h> */
+#endif
+
+#define MAXSTRINGLEN 80 /* Max string length */
+
+/* Local definitions: */
+#define MAX_MSG_SIZE (3*512) /* Maximum packet size */
+
+
+/*
+ * Return pointer to static string which gives full network error message.
+ */
+#define get_network_errmsg get_errmsg
+
+
+/*
+ * Data structure used to hold an arbitrary-lengthed list of IP addresses.
+ * The list may be shared among multiple hosts by setting the linkcount
+ * appropriately.
+ */
+
+struct in_addr_list {
+ unsigned int linkcount, addrcount;
+ struct in_addr addr[1]; /* Dynamically extended */
+};
+
+
+/*
+ * Data structures used to hold shared strings and shared binary data.
+ * The linkcount must be set appropriately.
+ */
+
+struct shared_string {
+ unsigned int linkcount;
+ char string[1]; /* Dynamically extended */
+};
+
+struct shared_bindata {
+ unsigned int linkcount, length;
+ byte data[1]; /* Dynamically extended */
+};
+
+
+/*
+ * Flag structure which indicates which symbols have been defined for a
+ * given host. This information is used to determine which data should or
+ * should not be reported in the bootp packet vendor info field.
+ */
+
+struct flag {
+ unsigned bootfile :1,
+ bootserver :1,
+ bootsize :1,
+ bootsize_auto :1,
+ cookie_server :1,
+ domain_server :1,
+ gateway :1,
+ generic :1,
+ haddr :1,
+ homedir :1,
+ htype :1,
+ impress_server :1,
+ iaddr :1,
+ log_server :1,
+ lpr_server :1,
+ name_server :1,
+ name_switch :1,
+ rlp_server :1,
+ send_name :1,
+ subnet_mask :1,
+ tftpdir :1,
+ time_offset :1,
+ time_server :1,
+ dump_file :1,
+ domain_name :1,
+ swap_server :1,
+ root_path :1,
+ exten_file :1,
+ reply_addr :1,
+ nis_domain :1,
+ nis_server :1,
+ ntp_server :1,
+ exec_file :1,
+ msg_size :1,
+ min_wait :1,
+ /* XXX - Add new tags here */
+ vm_cookie :1;
+};
+
+
+
+/*
+ * The flags structure contains TRUE flags for all the fields which
+ * are considered valid, regardless of whether they were explicitly
+ * specified or indirectly inferred from another entry.
+ *
+ * The gateway and the various server fields all point to a shared list of
+ * IP addresses.
+ *
+ * The hostname, home directory, and bootfile are all shared strings.
+ *
+ * The generic data field is a shared binary data structure. It is used to
+ * hold future RFC1048 vendor data until bootpd is updated to understand it.
+ *
+ * The vm_cookie field specifies the four-octet vendor magic cookie to use
+ * if it is desired to always send the same response to a given host.
+ *
+ * Hopefully, the rest is self-explanatory.
+ */
+
+struct host {
+ unsigned linkcount; /* hash list inserts */
+ struct flag flags; /* ALL valid fields */
+ struct in_addr_list *cookie_server,
+ *domain_server,
+ *gateway,
+ *impress_server,
+ *log_server,
+ *lpr_server,
+ *name_server,
+ *rlp_server,
+ *time_server,
+ *nis_server,
+ *ntp_server;
+ struct shared_string *bootfile,
+ *hostname,
+ *domain_name,
+ *homedir,
+ *tftpdir,
+ *dump_file,
+ *exten_file,
+ *root_path,
+ *nis_domain,
+ *exec_file;
+ struct shared_bindata *generic;
+ byte vm_cookie[4],
+ htype, /* RFC826 says this should be 16-bits but
+ RFC951 only allocates 1 byte. . . */
+ haddr[MAXHADDRLEN];
+ int32 time_offset;
+ u_int32 bootsize,
+ msg_size,
+ min_wait;
+ struct in_addr bootserver,
+ iaddr,
+ swap_server,
+ reply_addr,
+ subnet_mask;
+ /* XXX - Add new tags here (or above as appropriate) */
+};
+
+
+
+/*
+ * Variables shared among modules.
+ */
+
+extern int debug;
+extern char *bootptab;
+extern char *progname;
+
+extern u_char vm_cmu[4];
+extern u_char vm_rfc1048[4];
+
+extern hash_tbl *hwhashtable;
+extern hash_tbl *iphashtable;
+extern hash_tbl *nmhashtable;
+
diff --git a/libexec/bootpd/bootpgw/Makefile b/libexec/bootpd/bootpgw/Makefile
new file mode 100644
index 000000000000..c9b3feb04da6
--- /dev/null
+++ b/libexec/bootpd/bootpgw/Makefile
@@ -0,0 +1,11 @@
+# Makefile
+
+PROG= bootpgw
+MAN=
+SRCS= bootpgw.c getif.c hwaddr.c report.c rtmsg.c
+
+SRCDIR= ${.CURDIR}/..
+CFLAGS+=-I${SRCDIR}
+.PATH: ${SRCDIR}
+
+.include <bsd.prog.mk>
diff --git a/libexec/bootpd/bootpgw/Makefile.depend b/libexec/bootpd/bootpgw/Makefile.depend
new file mode 100644
index 000000000000..344a5d0e9310
--- /dev/null
+++ b/libexec/bootpd/bootpgw/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/bootpd/bootpgw/bootpgw.c b/libexec/bootpd/bootpgw/bootpgw.c
new file mode 100644
index 000000000000..168231002c0b
--- /dev/null
+++ b/libexec/bootpd/bootpgw/bootpgw.c
@@ -0,0 +1,674 @@
+/*
+ * bootpgw.c - BOOTP GateWay
+ * This program forwards BOOTP Request packets to a BOOTP server.
+ */
+
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+
+/*
+ * BOOTPGW is typically used to forward BOOTP client requests from
+ * one subnet to a BOOTP server on a different subnet.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+
+#include <err.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <paths.h>
+#include <syslog.h>
+#include <assert.h>
+
+#ifdef NO_SETSID
+# include <fcntl.h> /* for O_RDONLY, etc */
+#endif
+
+#include "bootp.h"
+#include "getif.h"
+#include "hwaddr.h"
+#include "report.h"
+#include "patchlevel.h"
+
+/* Local definitions: */
+#define MAX_MSG_SIZE (3*512) /* Maximum packet size */
+#define TRUE 1
+#define FALSE 0
+#define get_network_errmsg get_errmsg
+
+
+
+/*
+ * Externals, forward declarations, and global variables
+ */
+
+static void usage(void) __dead2;
+static void handle_reply(void);
+static void handle_request(void);
+
+/*
+ * IP port numbers for client and server obtained from /etc/services
+ */
+
+u_short bootps_port, bootpc_port;
+
+
+/*
+ * Internet socket and interface config structures
+ */
+
+struct sockaddr_in bind_addr; /* Listening */
+struct sockaddr_in recv_addr; /* Packet source */
+struct sockaddr_in send_addr; /* destination */
+
+
+/*
+ * option defaults
+ */
+int debug = 0; /* Debugging flag (level) */
+struct timeval actualtimeout =
+{ /* fifteen minutes */
+ 15 * 60L, /* tv_sec */
+ 0 /* tv_usec */
+};
+u_char maxhops = 4; /* Number of hops allowed for requests. */
+u_int minwait = 3; /* Number of seconds client must wait before
+ its bootrequest packets are forwarded. */
+int arpmod = TRUE; /* modify the ARP table */
+
+/*
+ * General
+ */
+
+int s; /* Socket file descriptor */
+char *pktbuf; /* Receive packet buffer */
+int pktlen;
+char *progname;
+char *servername;
+int32 server_ipa; /* Real server IP address, network order. */
+
+struct in_addr my_ip_addr;
+
+struct utsname my_uname;
+char *hostname;
+
+
+
+
+
+/*
+ * Initialization such as command-line processing is done and then the
+ * main server loop is started.
+ */
+
+int
+main(int argc, char **argv)
+{
+ struct timeval *timeout;
+ struct bootp *bp;
+ struct servent *servp;
+ struct hostent *hep;
+ char *stmp;
+ int n, ba_len, ra_len;
+ int nfound, readfds;
+ int standalone;
+
+ progname = strrchr(argv[0], '/');
+ if (progname) progname++;
+ else progname = argv[0];
+
+ /*
+ * Initialize logging.
+ */
+ report_init(0); /* uses progname */
+
+ /*
+ * Log startup
+ */
+ report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
+
+ /* Debugging for compilers with struct padding. */
+ assert(sizeof(struct bootp) == BP_MINPKTSZ);
+
+ /* Get space for receiving packets and composing replies. */
+ pktbuf = malloc(MAX_MSG_SIZE);
+ if (!pktbuf) {
+ report(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+ bp = (struct bootp *) pktbuf;
+
+ /*
+ * Check to see if a socket was passed to us from inetd.
+ *
+ * Use getsockname() to determine if descriptor 0 is indeed a socket
+ * (and thus we are probably a child of inetd) or if it is instead
+ * something else and we are running standalone.
+ */
+ s = 0;
+ ba_len = sizeof(bind_addr);
+ bzero((char *) &bind_addr, ba_len);
+ errno = 0;
+ standalone = TRUE;
+ if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
+ /*
+ * Descriptor 0 is a socket. Assume we are a child of inetd.
+ */
+ if (bind_addr.sin_family == AF_INET) {
+ standalone = FALSE;
+ bootps_port = ntohs(bind_addr.sin_port);
+ } else {
+ /* Some other type of socket? */
+ report(LOG_INFO, "getsockname: not an INET socket");
+ }
+ }
+ /*
+ * Set defaults that might be changed by option switches.
+ */
+ stmp = NULL;
+ timeout = &actualtimeout;
+
+ if (uname(&my_uname) < 0)
+ errx(1, "can't get hostname");
+ hostname = my_uname.nodename;
+
+ hep = gethostbyname(hostname);
+ if (!hep) {
+ printf("Can not get my IP address\n");
+ exit(1);
+ }
+ bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
+
+ /*
+ * Read switches.
+ */
+ for (argc--, argv++; argc > 0; argc--, argv++) {
+ if (argv[0][0] != '-')
+ break;
+ switch (argv[0][1]) {
+
+ case 'a': /* don't modify the ARP table */
+ arpmod = FALSE;
+ break;
+ case 'd': /* debug level */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else if (argv[1] && argv[1][0] == '-') {
+ /*
+ * Backwards-compatible behavior:
+ * no parameter, so just increment the debug flag.
+ */
+ debug++;
+ break;
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ warnx("invalid debug level");
+ break;
+ }
+ debug = n;
+ break;
+
+ case 'h': /* hop count limit */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
+ (n < 0) || (n > 16))
+ {
+ warnx("invalid hop count limit");
+ break;
+ }
+ maxhops = (u_char)n;
+ break;
+
+ case 'i': /* inetd mode */
+ standalone = FALSE;
+ break;
+
+ case 's': /* standalone mode */
+ standalone = TRUE;
+ break;
+
+ case 't': /* timeout */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ warnx("invalid timeout specification");
+ break;
+ }
+ actualtimeout.tv_sec = (int32) (60 * n);
+ /*
+ * If the actual timeout is zero, pass a NULL pointer
+ * to select so it blocks indefinitely, otherwise,
+ * point to the actual timeout value.
+ */
+ timeout = (n > 0) ? &actualtimeout : NULL;
+ break;
+
+ case 'w': /* wait time */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
+ (n < 0) || (n > 60))
+ {
+ warnx("invalid wait time");
+ break;
+ }
+ minwait = (u_int)n;
+ break;
+
+ default:
+ warnx("unknown switch: -%c", argv[0][1]);
+ usage();
+ break;
+
+ } /* switch */
+ } /* for args */
+
+ /* Make sure server name argument is suplied. */
+ servername = argv[0];
+ if (!servername) {
+ warnx("missing server name");
+ usage();
+ }
+ /*
+ * Get address of real bootp server.
+ */
+ if (isdigit(servername[0]))
+ server_ipa = inet_addr(servername);
+ else {
+ hep = gethostbyname(servername);
+ if (!hep)
+ errx(1, "can't get addr for %s", servername);
+ bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
+ }
+
+ if (standalone) {
+ /*
+ * Go into background and disassociate from controlling terminal.
+ * XXX - This is not the POSIX way (Should use setsid). -gwr
+ */
+ if (debug < 3) {
+ if (fork())
+ exit(0);
+#ifdef NO_SETSID
+ setpgrp(0,0);
+#ifdef TIOCNOTTY
+ n = open(_PATH_TTY, O_RDWR);
+ if (n >= 0) {
+ ioctl(n, TIOCNOTTY, (char *) 0);
+ (void) close(n);
+ }
+#endif /* TIOCNOTTY */
+#else /* SETSID */
+ if (setsid() < 0)
+ perror("setsid");
+#endif /* SETSID */
+ } /* if debug < 3 */
+ /*
+ * Nuke any timeout value
+ */
+ timeout = NULL;
+
+ /*
+ * Here, bootpd would do:
+ * chdir
+ * tzone_init
+ * rdtab_init
+ * readtab
+ */
+
+ /*
+ * Create a socket.
+ */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ report(LOG_ERR, "socket: %s", get_network_errmsg());
+ exit(1);
+ }
+ /*
+ * Get server's listening port number
+ */
+ servp = getservbyname("bootps", "udp");
+ if (servp) {
+ bootps_port = ntohs((u_short) servp->s_port);
+ } else {
+ bootps_port = (u_short) IPPORT_BOOTPS;
+ report(LOG_ERR,
+ "bootps/udp: unknown service -- using port %d",
+ bootps_port);
+ }
+
+ /*
+ * Bind socket to BOOTPS port.
+ */
+ bind_addr.sin_family = AF_INET;
+ bind_addr.sin_port = htons(bootps_port);
+ bind_addr.sin_addr.s_addr = INADDR_ANY;
+ if (bind(s, (struct sockaddr *) &bind_addr,
+ sizeof(bind_addr)) < 0)
+ {
+ report(LOG_ERR, "bind: %s", get_network_errmsg());
+ exit(1);
+ }
+ } /* if standalone */
+ /*
+ * Get destination port number so we can reply to client
+ */
+ servp = getservbyname("bootpc", "udp");
+ if (servp) {
+ bootpc_port = ntohs(servp->s_port);
+ } else {
+ report(LOG_ERR,
+ "bootpc/udp: unknown service -- using port %d",
+ IPPORT_BOOTPC);
+ bootpc_port = (u_short) IPPORT_BOOTPC;
+ }
+
+ /* no signal catchers */
+
+ /*
+ * Process incoming requests.
+ */
+ for (;;) {
+ struct timeval tv;
+
+ readfds = 1 << s;
+ if (timeout)
+ tv = *timeout;
+
+ nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
+ (timeout) ? &tv : NULL);
+ if (nfound < 0) {
+ if (errno != EINTR) {
+ report(LOG_ERR, "select: %s", get_errmsg());
+ }
+ continue;
+ }
+ if (!(readfds & (1 << s))) {
+ report(LOG_INFO, "exiting after %ld minutes of inactivity",
+ (long)(actualtimeout.tv_sec / 60));
+ exit(0);
+ }
+ ra_len = sizeof(recv_addr);
+ n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
+ (struct sockaddr *) &recv_addr, &ra_len);
+ if (n <= 0) {
+ continue;
+ }
+ if (debug > 3) {
+ report(LOG_INFO, "recvd pkt from IP addr %s",
+ inet_ntoa(recv_addr.sin_addr));
+ }
+ if (n < sizeof(struct bootp)) {
+ if (debug) {
+ report(LOG_INFO, "received short packet");
+ }
+ continue;
+ }
+ pktlen = n;
+
+ switch (bp->bp_op) {
+ case BOOTREQUEST:
+ handle_request();
+ break;
+ case BOOTREPLY:
+ handle_reply();
+ break;
+ }
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * Print "usage" message and exit
+ */
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: bootpgw [-a] [-i | -s] [-d level] [-h count] [-t timeout]\n"
+ " [-w time] server\n");
+ fprintf(stderr, "\t -a\tdon't modify ARP table\n");
+ fprintf(stderr, "\t -d n\tset debug level\n");
+ fprintf(stderr, "\t -h n\tset max hop count\n");
+ fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
+ fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
+ fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
+ fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
+ exit(1);
+}
+
+
+
+/*
+ * Process BOOTREQUEST packet.
+ *
+ * Note, this just forwards the request to a real server.
+ */
+static void
+handle_request()
+{
+ struct bootp *bp = (struct bootp *) pktbuf;
+ u_short secs;
+ u_char hops;
+
+ /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
+
+ if (debug) {
+ report(LOG_INFO, "request from %s",
+ inet_ntoa(recv_addr.sin_addr));
+ }
+ /* Has the client been waiting long enough? */
+ secs = ntohs(bp->bp_secs);
+ if (secs < minwait)
+ return;
+
+ /* Has this packet hopped too many times? */
+ hops = bp->bp_hops;
+ if (++hops > maxhops) {
+ report(LOG_NOTICE, "request from %s reached hop limit",
+ inet_ntoa(recv_addr.sin_addr));
+ return;
+ }
+ bp->bp_hops = hops;
+
+ /*
+ * Here one might discard a request from the same subnet as the
+ * real server, but we can assume that the real server will send
+ * a reply to the client before it waits for minwait seconds.
+ */
+
+ /* If gateway address is not set, put in local interface addr. */
+ if (bp->bp_giaddr.s_addr == 0) {
+#if 0 /* BUG */
+ struct sockaddr_in *sip;
+ struct ifreq *ifr;
+ /*
+ * XXX - This picks the wrong interface when the receive addr
+ * is the broadcast address. There is no portable way to
+ * find out which interface a broadcast was received on. -gwr
+ * (Thanks to <walker@zk3.dec.com> for finding this bug!)
+ */
+ ifr = getif(s, &recv_addr.sin_addr);
+ if (!ifr) {
+ report(LOG_NOTICE, "no interface for request from %s",
+ inet_ntoa(recv_addr.sin_addr));
+ return;
+ }
+ sip = (struct sockaddr_in *) &(ifr->ifr_addr);
+ bp->bp_giaddr = sip->sin_addr;
+#else /* BUG */
+ /*
+ * XXX - Just set "giaddr" to our "official" IP address.
+ * RFC 1532 says giaddr MUST be set to the address of the
+ * interface on which the request was received. Setting
+ * it to our "default" IP address is not strictly correct,
+ * but is good enough to allow the real BOOTP server to
+ * get the reply back here. Then, before we forward the
+ * reply to the client, the giaddr field is corrected.
+ * (In case the client uses giaddr, which it should not.)
+ * See handle_reply()
+ */
+ bp->bp_giaddr = my_ip_addr;
+#endif /* BUG */
+
+ /*
+ * XXX - DHCP says to insert a subnet mask option into the
+ * options area of the request (if vendor magic == std).
+ */
+ }
+ /* Set up socket address for send. */
+ send_addr.sin_family = AF_INET;
+ send_addr.sin_port = htons(bootps_port);
+ send_addr.sin_addr.s_addr = server_ipa;
+
+ /* Send reply with same size packet as request used. */
+ if (sendto(s, pktbuf, pktlen, 0,
+ (struct sockaddr *) &send_addr,
+ sizeof(send_addr)) < 0)
+ {
+ report(LOG_ERR, "sendto: %s", get_network_errmsg());
+ }
+}
+
+
+
+/*
+ * Process BOOTREPLY packet.
+ */
+static void
+handle_reply()
+{
+ struct bootp *bp = (struct bootp *) pktbuf;
+ struct ifreq *ifr;
+ struct sockaddr_in *sip;
+ unsigned char *ha;
+ int len, haf;
+
+ if (debug) {
+ report(LOG_INFO, " reply for %s",
+ inet_ntoa(bp->bp_yiaddr));
+ }
+ /* Make sure client is directly accessible. */
+ ifr = getif(s, &(bp->bp_yiaddr));
+ if (!ifr) {
+ report(LOG_NOTICE, "no interface for reply to %s",
+ inet_ntoa(bp->bp_yiaddr));
+ return;
+ }
+#if 1 /* Experimental (see BUG above) */
+/* #ifdef CATER_TO_OLD_CLIENTS ? */
+ /*
+ * The giaddr field has been set to our "default" IP address
+ * which might not be on the same interface as the client.
+ * In case the client looks at giaddr, (which it should not)
+ * giaddr is now set to the address of the correct interface.
+ */
+ sip = (struct sockaddr_in *) &(ifr->ifr_addr);
+ bp->bp_giaddr = sip->sin_addr;
+#endif
+
+ /* Set up socket address for send to client. */
+ send_addr.sin_family = AF_INET;
+ send_addr.sin_addr = bp->bp_yiaddr;
+ send_addr.sin_port = htons(bootpc_port);
+
+ if (arpmod) {
+ /* Create an ARP cache entry for the client. */
+ ha = bp->bp_chaddr;
+ len = bp->bp_hlen;
+ struct in_addr dst;
+
+ if (len > MAXHADDRLEN)
+ len = MAXHADDRLEN;
+ haf = (int) bp->bp_htype;
+ if (haf == 0)
+ haf = HTYPE_ETHERNET;
+
+ if (debug > 1)
+ report(LOG_INFO, "setarp %s - %s",
+ inet_ntoa(dst), haddrtoa(ha, len));
+ setarp(s, &dst, haf, ha, len);
+ }
+
+ /* Send reply with same size packet as request used. */
+ if (sendto(s, pktbuf, pktlen, 0,
+ (struct sockaddr *) &send_addr,
+ sizeof(send_addr)) < 0)
+ {
+ report(LOG_ERR, "sendto: %s", get_network_errmsg());
+ }
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/bootptab.5 b/libexec/bootpd/bootptab.5
new file mode 100644
index 000000000000..86158c9f02ff
--- /dev/null
+++ b/libexec/bootpd/bootptab.5
@@ -0,0 +1,428 @@
+.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University
+.\"
+.Dd October 31, 1991
+.Dt BOOTPTAB 5
+.Os
+.Sh NAME
+.Nm bootptab
+.Nd Internet Bootstrap Protocol server database
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration database file for
+.Xr bootpd 8 ,
+the Internet Bootstrap Protocol server.
+Its format is similar to that of
+.Xr termcap 5
+in which two-character case-sensitive tag symbols are used to
+represent host parameters.
+These parameter declarations are separated by
+colons (:), with a general format of:
+.Pp
+.Dl "hostname:tg=value. . . :tg=value. . . :tg=value. . . ."
+.Pp
+where
+.Em hostname
+is the actual name of a bootp client (or a "dummy entry"), and
+.Em tg
+is a two-character tag symbol.
+Dummy entries have an invalid hostname
+(one with a "." as the first character) and are used to provide
+default values used by other entries via the
+.Em tc=.dummy-entry
+mechanism.
+Most tags must be followed by an equals-sign
+and a value as above.
+Some may also appear in a boolean form with no
+value (i.e.\&
+.Em :tg: ) .
+The currently recognized tags are:
+.Pp
+.Bl -tag -width xxx -compact
+.It bf
+Bootfile
+.It bs
+Bootfile size in 512-octet blocks
+.It cs
+Cookie server address list
+.It df
+Merit dump file
+.It dn
+Domain name
+.It ds
+Domain name server address list
+.It ef
+Extension file
+.It gw
+Gateway address list
+.It ha
+Host hardware address
+.It hd
+Bootfile home directory
+.It hn
+Send client's hostname to client
+.It ht
+Host hardware type (see Assigned Numbers RFC)
+.It im
+Impress server address list
+.It ip
+Host IP address
+.It lg
+Log server address list
+.It lp
+LPR server address list
+.It ns
+IEN-116 name server address list
+.It nt
+NTP (time) Server (RFC 1129)
+.It ra
+Reply address override
+.It rl
+Resource location protocol server address list
+.It rp
+Root path to mount as root
+.It sa
+TFTP server address client should use
+.It sm
+Host subnet mask
+.It sw
+Swap server address
+.It tc
+Table continuation (points to similar "template" host entry)
+.It td
+TFTP root directory used by "secure" TFTP servers
+.It to
+Time offset in seconds from UTC
+.It ts
+Time server address list
+.It vm
+Vendor magic cookie selector
+.It yd
+YP (NIS) domain name
+.It ys
+YP (NIS) server address
+.El
+.Pp
+There is also a generic tag,
+.Pf T Em n ,
+where
+.Em n
+is an RFC1084 vendor field tag number.
+Thus it is possible to immediately
+take advantage of future extensions to RFC1084 without being forced to modify
+.Nm bootpd
+first.
+Generic data may be represented as either a stream of hexadecimal
+numbers or as a quoted string of
+.Tn ASCII
+characters.
+The length of the generic
+data is automatically determined and inserted into the proper field(s) of the
+RFC1084-style bootp reply.
+.Pp
+The following tags take a whitespace-separated list of IP addresses:
+.Em cs ,
+.Em ds ,
+.Em gw ,
+.Em im ,
+.Em lg ,
+.Em lp ,
+.Em ns ,
+.Em nt ,
+.Em ra ,
+.Em rl ,
+and
+.Em ts .
+The
+.Em ip ,
+.Em sa ,
+.Em sw ,
+.Em sm ,
+and
+.Em ys
+tags each take a single IP address.
+All IP addresses are specified in standard Internet "dot" notation
+and may use decimal, octal, or hexadecimal numbers
+(octal numbers begin with 0, hexadecimal numbers begin with '0x' or '0X').
+Any IP addresses may alternatively be specified as a hostname, causing
+.Nm bootpd
+to lookup the IP address for that host name using
+.Xr gethostbyname 3 .
+If the
+.Em ip
+tag is not specified,
+.Nm bootpd
+will determine the IP address using the entry name as the host name.
+(Dummy entries use an invalid host name to avoid automatic IP lookup.)
+.Pp
+The
+.Em ht
+tag specifies the hardware type code as either an unsigned decimal, octal, or
+hexadecimal integer or one of the following symbolic names:
+.Em ethernet
+or
+.Em ether
+for 10Mb Ethernet,
+.Em ethernet3
+or
+.Em ether3
+for 3Mb experimental Ethernet,
+.Em ieee802 ,
+.Em tr ,
+or
+.Em token-ring
+for IEEE 802 networks,
+.Em pronet
+for Proteon ProNET Token Ring, or
+.Em chaos ,
+.Em arcnet ,
+or
+.Em ax.25
+for Chaos, ARCNET, and AX.25 Amateur Radio networks, respectively.
+The
+.Em ha
+tag takes a hardware address which may be specified as a host name
+or in numeric form.
+Note that the numeric form
+.Em must
+be specified in hexadecimal; optional periods and/or a leading '0x' may be
+included for readability.
+The
+.Em ha
+tag must be preceded by the
+.Em ht
+tag (either explicitly or implicitly; see
+.Em tc
+below).
+If the hardware address is not specified and the type is specified
+as either "ethernet" or "ieee802", then
+.Nm bootpd
+will try to determine the hardware address using
+.Xr ether_hostton 3 .
+.Pp
+The hostname, home directory, and bootfile are
+.Tn ASCII
+strings which may be
+optionally surrounded by double quotes (").
+The client's request and the
+values of the
+.Em hd
+and
+.Em bf
+symbols determine how the server fills in the bootfile field of the bootp
+reply packet.
+.Pp
+If the client provides a file name it is left as is.
+Otherwise, if the
+.Em bf
+option is specified its value is copied into the reply packet.
+If the
+.Em hd
+option is specified as well, its value is prepended to the
+boot file copied into the reply packet.
+The existence of the boot file is checked only if the
+.Em bs Ns =auto
+option is used (to determine the boot file size).
+A reply may be sent whether or not the boot file exists.
+.Pp
+Some newer versions of
+.Xr tftpd 8
+provide a security feature to change their root directory using
+the
+.Xr chroot 2
+system call.
+The
+.Em td
+tag may be used to inform
+.Nm bootpd
+of this special root directory used by
+.Nm tftpd .
+(One may alternatively use the
+.Nm bootpd
+.Fl c Ar chdir
+option.)
+The
+.Em hd
+tag is actually relative to the root directory specified by the
+.Em td
+tag.
+For example, if the real absolute path to your BOOTP client bootfile is
+.Pa /tftpboot/bootfiles/bootimage ,
+and
+.Nm tftpd
+uses
+.Pa /tftpboot
+as its "secure" directory, then specify the following in
+.Pa bootptab :
+.Pp
+.Dl :td=/tftpboot:hd=/bootfiles:bf=bootimage:
+.Pp
+If your bootfiles are located directly in
+.Pa /tftpboot ,
+use:
+.Pp
+.Dl :td=/tftpboot:hd=/:bf=bootimage:
+.Pp
+The
+.Em sa
+tag may be used to specify the IP address of the particular TFTP server
+you wish the client to use.
+In the absence of this tag,
+.Nm bootpd
+will tell the client to perform TFTP to the same machine
+.Nm bootpd
+is running on.
+.Pp
+The time offset
+.Em to
+may be either a signed decimal integer specifying the client's
+time zone offset in seconds from UTC, or the keyword
+.Em auto
+which uses the server's time zone offset.
+Specifying the
+.Em to
+symbol as a boolean has the same effect as specifying
+.Em auto
+as its value.
+.Pp
+The bootfile size
+.Em bs
+may be either a decimal, octal, or hexadecimal integer specifying the size of
+the bootfile in 512-octet blocks, or the keyword
+.Em auto
+which causes the server to automatically calculate the bootfile size at each
+request.
+As with the time offset, specifying the
+.Em bs
+symbol as a boolean has the same effect as specifying
+.Em auto
+as its value.
+.Pp
+The vendor magic cookie selector (the
+.Em vm
+tag) may take one of the following keywords:
+.Em auto
+(indicating that vendor information is determined by the client's request),
+.Em rfc1048
+or
+.Em rfc1084
+(which always forces an RFC1084-style reply), or
+.Em cmu
+(which always forces a CMU-style reply).
+.Pp
+The
+.Em hn
+tag is strictly a boolean tag; it does not take the usual equals-sign and
+value.
+Its presence indicates that the hostname should be sent to RFC1084
+clients.
+.Nm Bootpd
+attempts to send the entire hostname as it is specified in the configuration
+file; if this will not fit into the reply packet, the name is shortened to
+just the host field (up to the first period, if present) and then tried.
+In no case is an arbitrarily-truncated hostname sent (if nothing reasonable
+will fit, nothing is sent).
+.Pp
+Often, many host entries share common values for certain tags (such as name
+servers, etc.).
+Rather than repeatedly specifying these tags, a full
+specification can be listed for one host entry and shared by others via the
+.Em tc
+(table continuation) mechanism.
+Often, the template entry is a dummy host which does not actually exist and
+never sends bootp requests.
+This feature is similar to the
+.Em tc
+feature of
+.Xr termcap 5
+for similar terminals.
+Note that
+.Nm bootpd
+allows the
+.Em tc
+tag symbol to appear anywhere in the host entry, unlike
+.Pa termcap
+which requires it to be the last tag.
+Information explicitly specified for a
+host always overrides information implied by a
+.Em tc
+tag symbol, regardless of its location within the entry.
+The
+value of the
+.Em tc
+tag may be the hostname or IP address of any host entry
+previously listed in the configuration file.
+.Pp
+Sometimes it is necessary to delete a specific tag after it has been inferred
+via
+.Em tc .
+This can be done using the construction
+.Em tag Ns @
+which removes the effect of
+.Em tag
+as in
+.Xr termcap 5 .
+For example, to completely undo an IEN-116 name server specification, use
+.Em :ns@:
+at an appropriate place in the configuration entry.
+After removal
+with
+.Em @ ,
+a tag is eligible to be set again through the
+.Em tc
+mechanism.
+.Pp
+Blank lines and lines beginning with "#" are ignored in the configuration
+file.
+Host entries are separated from one another by newlines; a single host
+entry may be extended over multiple lines if the lines end with a backslash
+(\\).
+It is also acceptable for lines to be longer than 80 characters.
+Tags
+may appear in any order, with the following exceptions: the hostname must be
+the very first field in an entry, and the hardware type must precede the
+hardware address.
+.Pp
+An example
+.Pa /etc/bootptab
+file follows:
+.Bd -literal -offset indent
+# Sample bootptab file (domain=andrew.cmu.edu)
+
+\&.default:\\
+ :hd=/usr/boot:bf=null:\\
+ :ds=netserver, lancaster:\\
+ :ns=pcs2, pcs1:\\
+ :ts=pcs2, pcs1:\\
+ :sm=255.255.255.0:\\
+ :gw=gw.cs.cmu.edu:\\
+ :hn:to=-18000:
+
+carnegie:ht=6:ha=7FF8100000AF:tc=.default:
+baldwin:ht=1:ha=0800200159C3:tc=.default:
+wylie:ht=1:ha=00DD00CADF00:tc=.default:
+arnold:ht=1:ha=0800200102AD:tc=.default:
+bairdford:ht=1:ha=08002B02A2F9:tc=.default:
+bakerstown:ht=1:ha=08002B0287C8:tc=.default:
+
+# Special domain name server and option tags for next host
+butlerjct:ha=08002001560D:ds=128.2.13.42:\\
+ :T37=0x12345927AD3BCF:\\
+ :T99="Special ASCII string":\\
+ :tc=.default:
+
+gastonville:ht=6:ha=7FFF81000A47:tc=.default:
+hahntown:ht=6:ha=7FFF81000434:tc=.default:
+hickman:ht=6:ha=7FFF810001BA:tc=.default:
+lowber:ht=1:ha=00DD00CAF000:tc=.default:
+mtoliver:ht=1:ha=00DD00FE1600:tc=.default:
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/bootptab -compact
+.It Pa /etc/bootptab
+.El
+.Sh "SEE ALSO"
+.Xr bootpd 8 ,
+.Xr tftpd 8
+.Pp
+DARPA Internet Request For Comments RFC951, RFC1048, RFC1084, Assigned Numbers
diff --git a/libexec/bootpd/bootptab.cmu b/libexec/bootpd/bootptab.cmu
new file mode 100644
index 000000000000..66212d421a23
--- /dev/null
+++ b/libexec/bootpd/bootptab.cmu
@@ -0,0 +1,124 @@
+# /etc/bootptab: database for bootp server (/etc/bootpd)
+# (I've hacked on this but can't test it... -gwr)
+
+# Blank lines and lines beginning with '#' are ignored.
+#
+# Legend: (see bootptab.5)
+# first field -- hostname (not indented)
+# bf -- bootfile
+# bs -- bootfile size in 512-octet blocks
+# cs -- cookie servers
+# df -- dump file name
+# dn -- domain name
+# ds -- domain name servers
+# ef -- extension file
+# gw -- gateways
+# ha -- hardware address
+# hd -- home directory for bootfiles
+# hn -- host name set for client
+# ht -- hardware type
+# im -- impress servers
+# ip -- host IP address
+# lg -- log servers
+# lp -- LPR servers
+# ns -- IEN-116 name servers
+# ra -- reply address
+# rl -- resource location protocol servers
+# rp -- root path
+# sa -- boot server address
+# sm -- subnet mask
+# sw -- swap server
+# tc -- template host (points to similar host entry)
+# td -- TFTP directory
+# to -- time offset (seconds)
+# ts -- time servers
+# vm -- vendor magic number
+# Tn -- generic option tag n
+#
+# Be careful about including backslashes where they're needed. Weird (bad)
+# things can happen when a backslash is omitted where one is intended.
+# Also, note that generic option data must be either a string or a
+# sequence of bytes where each byte is a two-digit hex value.
+
+# First, we define a global entry which specifies the stuff every host uses.
+# (Host name lookups are relative to the domain: andrew.cmu.edu)
+.default:\
+ :hn:dn=cmu.edu:\
+ :hd=/usr/boot:\
+ :ds=netserver, lancaster:\
+ :ns=pcs2, pcs1:\
+ :ts=pcs2, pcs1:\
+ :sm=255.255.0.0:\
+ :gw=gw.cs.cmu.edu:\
+ to=auto:
+
+
+# Next, we can define different master entries for each subnet. . .
+.subnet13 :sm=255.255.255.0:gw=128.2.13.1 :tc=.default:
+.subnet19 :sm=255.255.255.0:gw=128.2.19.1 :tc=.default:
+.subnet232 :sm=255.255.255.0:gw=128.2.232.1 :tc=.default:
+
+#
+# We should be able to use as many levels of indirection as desired. Use
+# your imagination. . .
+#
+
+
+# Individual entries (could also have different servers for some/all of these
+# hosts, but we don't really use this feature at CMU):
+
+carnegie:tc=.subnet13:ht=ieee802:ha=7FF8100000AF:
+baldwin:tc=.subnet19:ha=0800200159C3:
+wylie:tc=.subnet232:ha=00DD00CADF00:
+arnold:tc=.subnet19:ha=0800200102AD:
+bairdford:tc=.subnet19:ha=08002B02A2F9:
+bakerstown:tc=.subnet19:ha=08002B0287C8:
+butlerjct:tc=.subnet232:ha=08002001560D:
+gastonville:tc=.subnet232:ht=ieee802:ha=7FFF81000A47:
+hahntown:tc=.subnet13:ht=ieee802:ha=7FFF81000434:
+hickman:tc=.subnet19:ht=ieee802:ha=7FFF810001BA:
+lowber:tc=.subnet13:ha=00DD00CAF000:
+mtoliver:tc=.subnet19:ha=00DD00FE1600:
+osborne:tc=.subnet232:ha=00DD00CAD600:
+russelton:tc=.subnet232:ha=080020017FC3:
+thornburg:tc=.subnet13:ha=080020012A33:
+
+
+# Hmmm. . . Let's throw in some whitespace for readability. . . .
+
+andrew: tc=.subnet19:ha=00DD00C88900:
+birdville: tc=.subnet19:ha=00DD00FE2D00:
+coudersport: tc=.subnet13:ha=00DD00CB1E00:
+bridgeville: tc=.subnet232:ha=080020011394:
+franklin: tc=.subnet19:ha=08002B02A5D5:
+hollidaysburg: tc=.subnet19:ht=ieee802:ha=7FFF810002C8:
+honesdale: tc=.subnet19:ha=08002B02F83F:
+huntingdon: tc=.subnet19:ha=08002B02E410:
+indiana: tc=.subnet13:ha=08002B029BEC:
+jimthorpe: tc=.subnet232:ha=08002B02FBBA:
+kittanning: tc=.subnet232:ha=08002B0273FC:
+lebanon: tc=.subnet232:ha=08002B037F67:
+lewisburg: tc=.subnet19:ha=50005A1A0DE4:
+middleburg: tc=.subnet232:ha=00DD00FE1200:
+aspinwall: tc=.subnet13:ha=08002B03C163:
+berlin: tc=.subnet13:ha=00DD000A4400:
+norristown: tc=.subnet13:ha=08002001455B:
+pottsville: tc=.subnet13:ha=00DD000A3700:
+ridgway: tc=.subnet19:ha=08002B029425:
+scranton: tc=.subnet232:ha=0800200113A1:
+chalfont: tc=.subnet13:ha=08002001124B:
+washington: tc=.subnet19:ha=00DD00656E00:
+wellsboro: tc=.subnet13:ha=00DD00CB1C00:
+bb1: tc=.subnet19:ha=00DD000A1F00:
+adamstown: tc=.subnet13:ha=08002B02D0E6:
+beta: tc=.subnet19:ha=02070100B197:
+carbondale: tc=.subnet232:ha=08002B022A73:
+clairton: tc=.subnet19:ha=080020010FD1:
+egypt: tc=.subnet13:ha=00DD00847B00:
+fairchance: tc=.subnet232:ha=00DD000AB100:
+fairhope: tc=.subnet232:ha=00DD00CB0800:
+galeton: tc=.subnet232:ha=08002001138C:
+imperial: tc=.subnet232:ha=08002001130C:
+kingston: tc=.subnet232:ha=080020011382:
+knox: tc=.subnet232:ha=50005A1A0D2A:
+lakecity: tc=.subnet13:ha=080020011380:
diff --git a/libexec/bootpd/bootptab.mcs b/libexec/bootpd/bootptab.mcs
new file mode 100644
index 000000000000..1d5c78788038
--- /dev/null
+++ b/libexec/bootpd/bootptab.mcs
@@ -0,0 +1,90 @@
+# /etc/bootptab: database for bootp server (/etc/bootpd)
+# Last update: gwr, Sun Dec 12 19:00:00 EDT 1993
+# Blank lines and lines beginning with '#' are ignored.
+#
+#
+# Legend: (see bootptab.5)
+# first field -- hostname (not indented)
+# bf -- bootfile
+# bs -- bootfile size in 512-octet blocks
+# cs -- cookie servers
+# df -- dump file name
+# dn -- domain name
+# ds -- domain name servers
+# ef -- extension file
+# gw -- gateways
+# ha -- hardware address
+# hd -- home directory for bootfiles
+# hn -- host name set for client
+# ht -- hardware type
+# im -- impress servers
+# ip -- host IP address
+# lg -- log servers
+# lp -- LPR servers
+# ns -- IEN-116 name servers
+# ra -- reply address
+# rl -- resource location protocol servers
+# rp -- root path
+# sa -- boot server address
+# sm -- subnet mask
+# sw -- swap server
+# tc -- template host (points to similar host entry)
+# td -- TFTP directory
+# to -- time offset (seconds)
+# ts -- time servers
+# vm -- vendor magic number
+# Tn -- generic option tag n
+#
+# Be careful about including backslashes where they're needed. Weird (bad)
+# things can happen when a backslash is omitted where one is intended.
+# Also, note that generic option data must be either a string or a
+# sequence of bytes where each byte is a two-digit hex value.
+
+# First, we define a global entry which specifies the stuff every host uses.
+
+# If you leave "td" empty, run bootpd with the "-c /tftpboot" switch
+# so path names (boot files) will be interpreted relative to the same
+# directory as tftpd will use when opening files.
+.default:\
+ :hn:dn="mc.com":\
+ :td=/tftpboot:\
+ :ds=merlin, jericho:\
+ :to=auto:
+
+# Next, we can define different master entries for each subnet. . .
+
+.subnet16:\
+ :tc=.default:\
+ :sm=255.255.255.0:\
+ :gw=merlin:\
+ :sa=merlin:
+
+.subnet17:\
+ :tc=.default:\
+ :sm=255.255.255.0:\
+ :gw=merlin-gw:\
+ :sa=merlin-gw:
+
+#
+# We should be able to use as many levels of indirection as desired. Use
+# your imagination. . .
+#
+
+# Individual entries (could also have different servers for some/all of these
+# hosts, but we don't really use this feature at CMU):
+
+# Emulex terminal server
+emulex: tc=.subnet16:ha=00.00.C9.00.42.E0:bf=P4KTL0E:
+
+# Lantronix eps1
+eps1: tc=.subnet16:ha=00.80.A3.04.1D.78:
+
+# Tadpole 885 board.
+tp885: tc=.subnet17:ha=08.00.4C.00.2F.74:bf=tp885sys2.cfe:
+
+# MVME147 VxWorks board.
+#mvme147:tc=.subnet17:ha=08.00.3e.20.da.47:bf=mv147vxw.st:
+
+# These are just for testing
+bach: tc=.subnet16:ha="08:00:20:04:98:8d":bf=boot.sun4m:
+xanadu:tc=.subnet17:ha="00:80:42:42:04:c7":bf=boot.sun4c:
diff --git a/libexec/bootpd/bptypes.h b/libexec/bootpd/bptypes.h
new file mode 100644
index 000000000000..3e5deaec8bd5
--- /dev/null
+++ b/libexec/bootpd/bptypes.h
@@ -0,0 +1,20 @@
+#ifndef BPTYPES_H
+#define BPTYPES_H
+
+#include <sys/types.h>
+
+/*
+ * 32 bit integers are different types on various architectures
+ */
+
+#define int32 int32_t
+#define u_int32 u_int32_t
+
+/*
+ * Nice typedefs. . .
+ */
+
+typedef int boolean;
+typedef unsigned char byte;
+
+#endif /* BPTYPES_H */
diff --git a/libexec/bootpd/dovend.c b/libexec/bootpd/dovend.c
new file mode 100644
index 000000000000..b32459ae3347
--- /dev/null
+++ b/libexec/bootpd/dovend.c
@@ -0,0 +1,385 @@
+/*
+ * dovend.c : Inserts all but the first few vendor options.
+ */
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "bootp.h"
+#include "bootpd.h"
+#include "report.h"
+#include "dovend.h"
+
+PRIVATE int insert_generic(struct shared_bindata *, byte **, int *);
+
+/*
+ * Insert the 2nd part of the options into an option buffer.
+ * Return amount of space used.
+ *
+ * This inserts everything EXCEPT:
+ * magic cookie, subnet mask, gateway, bootsize, extension file
+ * Those are handled separately (in bootpd.c) to allow this function
+ * to be shared between bootpd and bootpef.
+ *
+ * When an "extension file" is in use, the options inserted by
+ * this function go into the exten_file, not the bootp response.
+ */
+
+int
+dovend_rfc1497(struct host *hp, byte *buf, int len)
+{
+ int bytesleft = len;
+ byte *vp = buf;
+
+ static const char noroom[] = "%s: No room for \"%s\" option";
+#define NEED(LEN, MSG) do \
+ if (bytesleft < (LEN)) { \
+ report(LOG_NOTICE, noroom, \
+ hp->hostname->string, MSG); \
+ return (vp - buf); \
+ } while (0)
+
+ /*
+ * Note that the following have already been inserted:
+ * magic_cookie, subnet_mask, gateway, bootsize
+ *
+ * The remaining options are inserted in order of importance.
+ * (Of course the importance of each is a matter of opinion.)
+ * The option insertion order should probably be configurable.
+ *
+ * This is the order used in the NetBSD version. Can anyone
+ * explain why the time_offset and swap_server are first?
+ * Also, why is the hostname so far down the list? -gwr
+ */
+
+ if (hp->flags.time_offset) {
+ NEED(6, "to");
+ *vp++ = TAG_TIME_OFFSET;/* -1 byte */
+ *vp++ = 4; /* -1 byte */
+ insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */
+ bytesleft -= 6;
+ }
+ /*
+ * swap server, root path, dump path
+ */
+ if (hp->flags.swap_server) {
+ NEED(6, "sw");
+ /* There is just one SWAP_SERVER, so it is not an iplist. */
+ *vp++ = TAG_SWAP_SERVER;/* -1 byte */
+ *vp++ = 4; /* -1 byte */
+ insert_u_long(hp->swap_server.s_addr, &vp); /* -4 bytes */
+ bytesleft -= 6; /* Fix real count */
+ }
+ if (hp->flags.root_path) {
+ /*
+ * Check for room for root_path. Add 2 to account for
+ * TAG_ROOT_PATH and length.
+ */
+ len = strlen(hp->root_path->string);
+ NEED((len + 2), "rp");
+ *vp++ = TAG_ROOT_PATH;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->root_path->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ if (hp->flags.dump_file) {
+ /*
+ * Check for room for dump_file. Add 2 to account for
+ * TAG_DUMP_FILE and length.
+ */
+ len = strlen(hp->dump_file->string);
+ NEED((len + 2), "df");
+ *vp++ = TAG_DUMP_FILE;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->dump_file->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ /*
+ * DNS server and domain
+ */
+ if (hp->flags.domain_server) {
+ if (insert_ip(TAG_DOMAIN_SERVER,
+ hp->domain_server,
+ &vp, &bytesleft))
+ NEED(8, "ds");
+ }
+ if (hp->flags.domain_name) {
+ /*
+ * Check for room for domain_name. Add 2 to account for
+ * TAG_DOMAIN_NAME and length.
+ */
+ len = strlen(hp->domain_name->string);
+ NEED((len + 2), "dn");
+ *vp++ = TAG_DOMAIN_NAME;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->domain_name->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ /*
+ * NIS (YP) server and domain
+ */
+ if (hp->flags.nis_server) {
+ if (insert_ip(TAG_NIS_SERVER,
+ hp->nis_server,
+ &vp, &bytesleft))
+ NEED(8, "ys");
+ }
+ if (hp->flags.nis_domain) {
+ /*
+ * Check for room for nis_domain. Add 2 to account for
+ * TAG_NIS_DOMAIN and length.
+ */
+ len = strlen(hp->nis_domain->string);
+ NEED((len + 2), "yn");
+ *vp++ = TAG_NIS_DOMAIN;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->nis_domain->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ /* IEN 116 name server */
+ if (hp->flags.name_server) {
+ if (insert_ip(TAG_NAME_SERVER,
+ hp->name_server,
+ &vp, &bytesleft))
+ NEED(8, "ns");
+ }
+ if (hp->flags.rlp_server) {
+ if (insert_ip(TAG_RLP_SERVER,
+ hp->rlp_server,
+ &vp, &bytesleft))
+ NEED(8, "rl");
+ }
+ /* Time server (RFC 868) */
+ if (hp->flags.time_server) {
+ if (insert_ip(TAG_TIME_SERVER,
+ hp->time_server,
+ &vp, &bytesleft))
+ NEED(8, "ts");
+ }
+ /* NTP (time) Server (RFC 1129) */
+ if (hp->flags.ntp_server) {
+ if (insert_ip(TAG_NTP_SERVER,
+ hp->ntp_server,
+ &vp, &bytesleft))
+ NEED(8, "nt");
+ }
+ /*
+ * I wonder: If the hostname were "promoted" into the BOOTP
+ * response part, might these "extension" files possibly be
+ * shared between several clients?
+ *
+ * Also, why not just use longer BOOTP packets with all the
+ * additional length used as option data. This bootpd version
+ * already supports that feature by replying with the same
+ * packet length as the client request packet. -gwr
+ */
+ if (hp->flags.name_switch && hp->flags.send_name) {
+ /*
+ * Check for room for hostname. Add 2 to account for
+ * TAG_HOST_NAME and length.
+ */
+ len = strlen(hp->hostname->string);
+#if 0
+ /*
+ * XXX - Too much magic. The user can always set the hostname
+ * to the short version in the bootptab file. -gwr
+ */
+ if ((len + 2) > bytesleft) {
+ /*
+ * Not enough room for full (domain-qualified) hostname, try
+ * stripping it down to just the first field (host).
+ */
+ char *tmpstr = hp->hostname->string;
+ len = 0;
+ while (*tmpstr && (*tmpstr != '.')) {
+ tmpstr++;
+ len++;
+ }
+ }
+#endif
+ NEED((len + 2), "hn");
+ *vp++ = TAG_HOST_NAME;
+ *vp++ = (byte) (len & 0xFF);
+ bcopy(hp->hostname->string, vp, len);
+ vp += len;
+ bytesleft -= len + 2;
+ }
+ /*
+ * The rest of these are less important, so they go last.
+ */
+ if (hp->flags.lpr_server) {
+ if (insert_ip(TAG_LPR_SERVER,
+ hp->lpr_server,
+ &vp, &bytesleft))
+ NEED(8, "lp");
+ }
+ if (hp->flags.cookie_server) {
+ if (insert_ip(TAG_COOKIE_SERVER,
+ hp->cookie_server,
+ &vp, &bytesleft))
+ NEED(8, "cs");
+ }
+ if (hp->flags.log_server) {
+ if (insert_ip(TAG_LOG_SERVER,
+ hp->log_server,
+ &vp, &bytesleft))
+ NEED(8, "lg");
+ }
+ /*
+ * XXX - Add new tags here (to insert options)
+ */
+ if (hp->flags.generic) {
+ if (insert_generic(hp->generic, &vp, &bytesleft))
+ NEED(64, "(generic)");
+ }
+ /*
+ * The end marker is inserted by the caller.
+ */
+ return (vp - buf);
+#undef NEED
+} /* dovend_rfc1497 */
+
+
+
+/*
+ * Insert a tag value, a length value, and a list of IP addresses into the
+ * memory buffer indirectly pointed to by "dest". "tag" is the RFC1048 tag
+ * number to use, "iplist" is a pointer to a list of IP addresses
+ * (struct in_addr_list), and "bytesleft" points to an integer which
+ * indicates the size of the "dest" buffer.
+ *
+ * Return zero if everything fits.
+ *
+ * This is used to fill the vendor-specific area of a bootp packet in
+ * conformance to RFC1048.
+ */
+
+int
+insert_ip(byte tag, struct in_addr_list *iplist, byte **dest, int *bytesleft)
+{
+ struct in_addr *addrptr;
+ unsigned addrcount = 1;
+ byte *d;
+
+ if (iplist == NULL)
+ return (0);
+
+ if (*bytesleft >= 6) {
+ d = *dest; /* Save pointer for later */
+ **dest = tag;
+ (*dest) += 2;
+ (*bytesleft) -= 2; /* Account for tag and length */
+ addrptr = iplist->addr;
+ addrcount = iplist->addrcount;
+ while ((*bytesleft >= 4) && (addrcount > 0)) {
+ insert_u_long(addrptr->s_addr, dest);
+ addrptr++;
+ addrcount--;
+ (*bytesleft) -= 4; /* Four bytes per address */
+ }
+ d[1] = (byte) ((*dest - d - 2) & 0xFF);
+ }
+ return (addrcount);
+}
+
+
+
+/*
+ * Insert generic data into a bootp packet. The data is assumed to already
+ * be in RFC1048 format. It is inserted using a first-fit algorithm which
+ * attempts to insert as many tags as possible. Tags and data which are
+ * too large to fit are skipped; any remaining tags are tried until they
+ * have all been exhausted.
+ * Return zero if everything fits.
+ */
+
+static int
+insert_generic(struct shared_bindata *gendata, byte **buff, int *bytesleft)
+{
+ byte *srcptr;
+ int length, numbytes;
+ int skipped = 0;
+
+ if (gendata == NULL)
+ return (0);
+
+ srcptr = gendata->data;
+ length = gendata->length;
+ while ((length > 0) && (*bytesleft > 0)) {
+ switch (*srcptr) {
+ case TAG_END:
+ length = 0; /* Force an exit on next iteration */
+ break;
+ case TAG_PAD:
+ *(*buff)++ = *srcptr++;
+ (*bytesleft)--;
+ length--;
+ break;
+ default:
+ numbytes = srcptr[1] + 2;
+ if (*bytesleft < numbytes)
+ skipped += numbytes;
+ else {
+ bcopy(srcptr, *buff, numbytes);
+ (*buff) += numbytes;
+ (*bytesleft) -= numbytes;
+ }
+ srcptr += numbytes;
+ length -= numbytes;
+ break;
+ }
+ } /* while */
+ return (skipped);
+}
+
+/*
+ * Insert the unsigned long "value" into memory starting at the byte
+ * pointed to by the byte pointer (*dest). (*dest) is updated to
+ * point to the next available byte.
+ *
+ * Since it is desirable to internally store network addresses in network
+ * byte order (in struct in_addr's), this routine expects longs to be
+ * passed in network byte order.
+ *
+ * However, due to the nature of the main algorithm, the long must be in
+ * host byte order, thus necessitating the use of ntohl() first.
+ */
+
+void
+insert_u_long(u_int32 value, byte **dest)
+{
+ byte *temp;
+ int n;
+
+ value = ntohl(value); /* Must use host byte order here */
+ temp = (*dest += 4);
+ for (n = 4; n > 0; n--) {
+ *--temp = (byte) (value & 0xFF);
+ value >>= 8;
+ }
+ /* Final result is network byte order */
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/dovend.h b/libexec/bootpd/dovend.h
new file mode 100644
index 000000000000..4b9f7d6baa04
--- /dev/null
+++ b/libexec/bootpd/dovend.h
@@ -0,0 +1,5 @@
+/* dovend.h */
+
+extern int dovend_rfc1497(struct host *hp, u_char *buf, int len);
+extern int insert_ip(byte, struct in_addr_list *, byte **, int *);
+extern void insert_u_long(u_int32, u_char **);
diff --git a/libexec/bootpd/dumptab.c b/libexec/bootpd/dumptab.c
new file mode 100644
index 000000000000..9b839c26c5b9
--- /dev/null
+++ b/libexec/bootpd/dumptab.c
@@ -0,0 +1,361 @@
+/*
+ * dumptab.c - handles dumping the database
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <syslog.h>
+#include <time.h>
+
+#include "bootp.h"
+#include "hash.h"
+#include "hwaddr.h"
+#include "report.h"
+#include "patchlevel.h"
+#include "bootpd.h"
+
+#ifdef DEBUG
+static void dump_generic(FILE *, struct shared_bindata *);
+static void dump_host(FILE *, struct host *);
+static void list_ipaddresses(FILE *, struct in_addr_list *);
+#endif
+
+#ifndef DEBUG
+void
+dumptab(char *filename)
+{
+ report(LOG_INFO, "No dumptab support!");
+}
+
+#else /* DEBUG */
+
+/*
+ * Dump the internal memory database to bootpd_dump.
+ */
+
+void
+dumptab(char *filename)
+{
+ int n;
+ struct host *hp;
+ FILE *fp;
+ time_t t;
+ /* Print symbols in alphabetical order for reader's convenience. */
+ static char legend[] = "#\n# Legend:\t(see bootptab.5)\n\
+#\tfirst field -- hostname (not indented)\n\
+#\tbf -- bootfile\n\
+#\tbs -- bootfile size in 512-octet blocks\n\
+#\tcs -- cookie servers\n\
+#\tdf -- dump file name\n\
+#\tdn -- domain name\n\
+#\tds -- domain name servers\n\
+#\tef -- extension file\n\
+#\tex -- exec file (YORK_EX_OPTION)\n\
+#\tgw -- gateways\n\
+#\tha -- hardware address\n\
+#\thd -- home directory for bootfiles\n\
+#\thn -- host name set for client\n\
+#\tht -- hardware type\n\
+#\tim -- impress servers\n\
+#\tip -- host IP address\n\
+#\tlg -- log servers\n\
+#\tlp -- LPR servers\n\
+#\tms -- message size\n\
+#\tmw -- min wait (secs)\n\
+#\tns -- IEN-116 name servers\n\
+#\tnt -- NTP servers (RFC 1129)\n\
+#\tra -- reply address override\n\
+#\trl -- resource location protocol servers\n\
+#\trp -- root path\n\
+#\tsa -- boot server address\n\
+#\tsm -- subnet mask\n\
+#\tsw -- swap server\n\
+#\ttc -- template host (points to similar host entry)\n\
+#\ttd -- TFTP directory\n\
+#\tto -- time offset (seconds)\n\
+#\tts -- time servers\n\
+#\tvm -- vendor magic number\n\
+#\tyd -- YP (NIS) domain\n\
+#\tys -- YP (NIS) servers\n\
+#\tTn -- generic option tag n\n\
+\n";
+
+ /*
+ * Open bootpd.dump file.
+ */
+ if ((fp = fopen(filename, "w")) == NULL) {
+ report(LOG_ERR, "error opening \"%s\": %s",
+ filename, get_errmsg());
+ exit(1);
+ }
+ t = time(NULL);
+ fprintf(fp, "\n# %s %s.%d\n", progname, VERSION, PATCHLEVEL);
+ fprintf(fp, "# %s: dump of bootp server database.\n", filename);
+ fprintf(fp, "# Dump taken %s", ctime(&t));
+ fwrite(legend, 1, sizeof(legend) - 1, fp);
+
+ n = 0;
+ for (hp = (struct host *) hash_FirstEntry(nmhashtable); hp != NULL;
+ hp = (struct host *) hash_NextEntry(nmhashtable)) {
+ dump_host(fp, hp);
+ fprintf(fp, "\n");
+ n++;
+ }
+ fclose(fp);
+
+ report(LOG_INFO, "dumped %d entries to \"%s\".", n, filename);
+}
+
+
+
+/*
+ * Dump all the available information on the host pointed to by "hp".
+ * The output is sent to the file pointed to by "fp".
+ */
+
+static void
+dump_host(FILE *fp, struct host *hp)
+{
+ /* Print symbols in alphabetical order for reader's convenience. */
+ if (hp) {
+ fprintf(fp, "%s:", (hp->hostname ?
+ hp->hostname->string : "?"));
+ if (hp->flags.bootfile) {
+ fprintf(fp, "\\\n\t:bf=%s:", hp->bootfile->string);
+ }
+ if (hp->flags.bootsize) {
+ fprintf(fp, "\\\n\t:bs=");
+ if (hp->flags.bootsize_auto) {
+ fprintf(fp, "auto:");
+ } else {
+ fprintf(fp, "%lu:", (u_long)hp->bootsize);
+ }
+ }
+ if (hp->flags.cookie_server) {
+ fprintf(fp, "\\\n\t:cs=");
+ list_ipaddresses(fp, hp->cookie_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.dump_file) {
+ fprintf(fp, "\\\n\t:df=%s:", hp->dump_file->string);
+ }
+ if (hp->flags.domain_name) {
+ fprintf(fp, "\\\n\t:dn=%s:", hp->domain_name->string);
+ }
+ if (hp->flags.domain_server) {
+ fprintf(fp, "\\\n\t:ds=");
+ list_ipaddresses(fp, hp->domain_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.exten_file) {
+ fprintf(fp, "\\\n\t:ef=%s:", hp->exten_file->string);
+ }
+ if (hp->flags.exec_file) {
+ fprintf(fp, "\\\n\t:ex=%s:", hp->exec_file->string);
+ }
+ if (hp->flags.gateway) {
+ fprintf(fp, "\\\n\t:gw=");
+ list_ipaddresses(fp, hp->gateway);
+ fprintf(fp, ":");
+ }
+ /* FdC: swap_server (see below) */
+ if (hp->flags.homedir) {
+ fprintf(fp, "\\\n\t:hd=%s:", hp->homedir->string);
+ }
+ /* FdC: dump_file (see above) */
+ /* FdC: domain_name (see above) */
+ /* FdC: root_path (see below) */
+ if (hp->flags.name_switch && hp->flags.send_name) {
+ fprintf(fp, "\\\n\t:hn:");
+ }
+ if (hp->flags.htype) {
+ int hlen = haddrlength(hp->htype);
+ fprintf(fp, "\\\n\t:ht=%u:", (unsigned) hp->htype);
+ if (hp->flags.haddr) {
+ fprintf(fp, "ha=\"%s\":",
+ haddrtoa(hp->haddr, hlen));
+ }
+ }
+ if (hp->flags.impress_server) {
+ fprintf(fp, "\\\n\t:im=");
+ list_ipaddresses(fp, hp->impress_server);
+ fprintf(fp, ":");
+ }
+ /* NetBSD: swap_server (see below) */
+ if (hp->flags.iaddr) {
+ fprintf(fp, "\\\n\t:ip=%s:", inet_ntoa(hp->iaddr));
+ }
+ if (hp->flags.log_server) {
+ fprintf(fp, "\\\n\t:lg=");
+ list_ipaddresses(fp, hp->log_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.lpr_server) {
+ fprintf(fp, "\\\n\t:lp=");
+ list_ipaddresses(fp, hp->lpr_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.msg_size) {
+ fprintf(fp, "\\\n\t:ms=%lu:", (u_long)hp->msg_size);
+ }
+ if (hp->flags.min_wait) {
+ fprintf(fp, "\\\n\t:mw=%lu:", (u_long)hp->min_wait);
+ }
+ if (hp->flags.name_server) {
+ fprintf(fp, "\\\n\t:ns=");
+ list_ipaddresses(fp, hp->name_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.ntp_server) {
+ fprintf(fp, "\\\n\t:nt=");
+ list_ipaddresses(fp, hp->ntp_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.reply_addr) {
+ fprintf(fp, "\\\n\t:ra=%s:", inet_ntoa(hp->reply_addr));
+ }
+ if (hp->flags.rlp_server) {
+ fprintf(fp, "\\\n\t:rl=");
+ list_ipaddresses(fp, hp->rlp_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.root_path) {
+ fprintf(fp, "\\\n\t:rp=%s:", hp->root_path->string);
+ }
+ if (hp->flags.bootserver) {
+ fprintf(fp, "\\\n\t:sa=%s:", inet_ntoa(hp->bootserver));
+ }
+ if (hp->flags.subnet_mask) {
+ fprintf(fp, "\\\n\t:sm=%s:", inet_ntoa(hp->subnet_mask));
+ }
+ if (hp->flags.swap_server) {
+ fprintf(fp, "\\\n\t:sw=%s:", inet_ntoa(hp->subnet_mask));
+ }
+ if (hp->flags.tftpdir) {
+ fprintf(fp, "\\\n\t:td=%s:", hp->tftpdir->string);
+ }
+ /* NetBSD: rootpath (see above) */
+ /* NetBSD: domainname (see above) */
+ /* NetBSD: dumpfile (see above) */
+ if (hp->flags.time_offset) {
+ fprintf(fp, "\\\n\t:to=%ld:", (long)hp->time_offset);
+ }
+ if (hp->flags.time_server) {
+ fprintf(fp, "\\\n\t:ts=");
+ list_ipaddresses(fp, hp->time_server);
+ fprintf(fp, ":");
+ }
+ if (hp->flags.vm_cookie) {
+ fprintf(fp, "\\\n\t:vm=");
+ if (!bcmp(hp->vm_cookie, vm_rfc1048, 4)) {
+ fprintf(fp, "rfc1048:");
+ } else if (!bcmp(hp->vm_cookie, vm_cmu, 4)) {
+ fprintf(fp, "cmu:");
+ } else {
+ fprintf(fp, "%d.%d.%d.%d:",
+ (int) ((hp->vm_cookie)[0]),
+ (int) ((hp->vm_cookie)[1]),
+ (int) ((hp->vm_cookie)[2]),
+ (int) ((hp->vm_cookie)[3]));
+ }
+ }
+ if (hp->flags.nis_domain) {
+ fprintf(fp, "\\\n\t:yd=%s:",
+ hp->nis_domain->string);
+ }
+ if (hp->flags.nis_server) {
+ fprintf(fp, "\\\n\t:ys=");
+ list_ipaddresses(fp, hp->nis_server);
+ fprintf(fp, ":");
+ }
+ /*
+ * XXX - Add new tags here (or above,
+ * so they print in alphabetical order).
+ */
+
+ if (hp->flags.generic) {
+ dump_generic(fp, hp->generic);
+ }
+ }
+}
+
+
+static void
+dump_generic(FILE *fp, struct shared_bindata *generic)
+{
+ u_char *bp = generic->data;
+ u_char *ep = bp + generic->length;
+ u_char tag;
+ int len;
+
+ while (bp < ep) {
+ tag = *bp++;
+ if (tag == TAG_PAD)
+ continue;
+ if (tag == TAG_END)
+ return;
+ len = *bp++;
+ if (bp + len > ep) {
+ fprintf(fp, " #junk in generic! :");
+ return;
+ }
+ fprintf(fp, "\\\n\t:T%d=", tag);
+ while (len) {
+ fprintf(fp, "%02X", *bp);
+ bp++;
+ len--;
+ if (len)
+ fprintf(fp, ".");
+ }
+ fprintf(fp, ":");
+ }
+}
+
+
+
+/*
+ * Dump an entire struct in_addr_list of IP addresses to the indicated file.
+ *
+ * The addresses are printed in standard ASCII "dot" notation and separated
+ * from one another by a single space. A single leading space is also
+ * printed before the first address.
+ *
+ * Null lists produce no output (and no error).
+ */
+
+static void
+list_ipaddresses(FILE *fp, struct in_addr_list *ipptr)
+{
+ unsigned count;
+ struct in_addr *addrptr;
+
+ if (ipptr) {
+ count = ipptr->addrcount;
+ addrptr = ipptr->addr;
+ while (count > 0) {
+ fprintf(fp, "%s", inet_ntoa(*addrptr++));
+ count--;
+ if (count)
+ fprintf(fp, ", ");
+ }
+ }
+}
+
+#endif /* DEBUG */
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/getether.c b/libexec/bootpd/getether.c
new file mode 100644
index 000000000000..1a49ce574d38
--- /dev/null
+++ b/libexec/bootpd/getether.c
@@ -0,0 +1,385 @@
+/*
+ * getether.c : get the ethernet address of an interface
+ *
+ * All of this code is quite system-specific. As you may well
+ * guess, it took a good bit of detective work to figure out!
+ *
+ * If you figure out how to do this on another system,
+ * please let me know. <gwr@mc.com>
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+
+#include <ctype.h>
+#include <paths.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "getether.h"
+#include "report.h"
+#define EALEN 6
+
+#if defined(ultrix) || (defined(__osf__) && defined(__alpha))
+/*
+ * This is really easy on Ultrix! Thanks to
+ * Harald Lundberg <hl@tekla.fi> for this code.
+ *
+ * The code here is not specific to the Alpha, but that was the
+ * only symbol we could find to identify DEC's version of OSF.
+ * (Perhaps we should just define DEC in the Makefile... -gwr)
+ */
+
+#include <sys/ioctl.h>
+#include <net/if.h> /* struct ifdevea */
+
+getether(ifname, eap)
+ char *ifname, *eap;
+{
+ int rc = -1;
+ int fd;
+ struct ifdevea phys;
+ bzero(&phys, sizeof(phys));
+ strcpy(phys.ifr_name, ifname);
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ report(LOG_ERR, "getether: socket(INET,DGRAM) failed");
+ return -1;
+ }
+ if (ioctl(fd, SIOCRPHYSADDR, &phys) < 0) {
+ report(LOG_ERR, "getether: ioctl SIOCRPHYSADDR failed");
+ } else {
+ bcopy(&phys.current_pa[0], eap, EALEN);
+ rc = 0;
+ }
+ close(fd);
+ return rc;
+}
+
+#define GETETHER
+#endif /* ultrix|osf1 */
+
+
+#ifdef SUNOS
+
+#include <sys/sockio.h>
+#include <sys/time.h> /* needed by net_if.h */
+#include <net/nit_if.h> /* for NIOCBIND */
+#include <net/if.h> /* for struct ifreq */
+
+getether(ifname, eap)
+ char *ifname; /* interface name from ifconfig structure */
+ char *eap; /* Ether address (output) */
+{
+ int rc = -1;
+
+ struct ifreq ifrnit;
+ int nit;
+
+ bzero((char *) &ifrnit, sizeof(ifrnit));
+ strlcpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ);
+
+ nit = open("/dev/nit", 0);
+ if (nit < 0) {
+ report(LOG_ERR, "getether: open /dev/nit: %s",
+ get_errmsg());
+ return rc;
+ }
+ do {
+ if (ioctl(nit, NIOCBIND, &ifrnit) < 0) {
+ report(LOG_ERR, "getether: NIOCBIND on nit");
+ break;
+ }
+ if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) {
+ report(LOG_ERR, "getether: SIOCGIFADDR on nit");
+ break;
+ }
+ bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN);
+ rc = 0;
+ } while (0);
+ close(nit);
+ return rc;
+}
+
+#define GETETHER
+#endif /* SUNOS */
+
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+/* Thanks to John Brezak <brezak@ch.hp.com> for this code. */
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+int
+getether(char *ifname, char *eap)
+{
+ int fd, rc = -1;
+ int n;
+ struct ifreq ibuf[16];
+ struct ifconf ifc;
+ struct ifreq *ifrp, *ifend;
+
+ /* Fetch the interface configuration */
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg());
+ return (fd);
+ }
+ ifc.ifc_len = sizeof(ibuf);
+ ifc.ifc_buf = (caddr_t) ibuf;
+ if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 ||
+ ifc.ifc_len < sizeof(struct ifreq)) {
+ report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg());
+ goto out;
+ }
+ /* Search interface configuration list for link layer address. */
+ ifrp = ibuf;
+ ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len);
+ while (ifrp < ifend) {
+ /* Look for interface */
+ if (strcmp(ifname, ifrp->ifr_name) == 0 &&
+ ifrp->ifr_addr.sa_family == AF_LINK &&
+ ((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) {
+ bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN);
+ rc = 0;
+ break;
+ }
+ /* Bump interface config pointer */
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ n = sizeof(*ifrp);
+ ifrp = (struct ifreq *) ((char *) ifrp + n);
+ }
+
+ out:
+ close(fd);
+ return (rc);
+}
+
+#define GETETHER
+#endif /* __NetBSD__ */
+
+
+#ifdef SVR4
+/*
+ * This is for "Streams TCP/IP" by Lachman Associates.
+ * They sure made this cumbersome! -gwr
+ */
+
+#include <sys/sockio.h>
+#include <sys/dlpi.h>
+#include <stropts.h>
+#include <string.h>
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getether(ifname, eap)
+ char *ifname; /* interface name from ifconfig structure */
+ char *eap; /* Ether address (output) */
+{
+ int rc = -1;
+ char devname[32];
+ char tmpbuf[sizeof(union DL_primitives) + 16];
+ struct strbuf cbuf;
+ int fd, flags;
+ union DL_primitives *dlp;
+ char *enaddr;
+ int unit = -1; /* which unit to attach */
+
+ snprintf(devname, sizeof(devname), "%s%s", _PATH_DEV, ifname);
+ fd = open(devname, 2);
+ if (fd < 0) {
+ /* Try without the trailing digit. */
+ char *p = devname + 5;
+ while (isalpha(*p))
+ p++;
+ if (isdigit(*p)) {
+ unit = *p - '0';
+ *p = '\0';
+ }
+ fd = open(devname, 2);
+ if (fd < 0) {
+ report(LOG_ERR, "getether: open %s: %s",
+ devname, get_errmsg());
+ return rc;
+ }
+ }
+#ifdef DL_ATTACH_REQ
+ /*
+ * If this is a "Style 2" DLPI, then we must "attach" first
+ * to tell the driver which unit (board, port) we want.
+ * For now, decide this based on the device name.
+ * (Should do "info_req" and check dl_provider_style ...)
+ */
+ if (unit >= 0) {
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ dlp = (union DL_primitives *) tmpbuf;
+ dlp->dl_primitive = DL_ATTACH_REQ;
+ dlp->attach_req.dl_ppa = unit;
+ cbuf.buf = tmpbuf;
+ cbuf.len = DL_ATTACH_REQ_SIZE;
+ if (putmsg(fd, &cbuf, NULL, 0) < 0) {
+ report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg());
+ goto out;
+ }
+ /* Recv the ack. */
+ cbuf.buf = tmpbuf;
+ cbuf.maxlen = sizeof(tmpbuf);
+ flags = 0;
+ if (getmsg(fd, &cbuf, NULL, &flags) < 0) {
+ report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg());
+ goto out;
+ }
+ /*
+ * Check the type, etc.
+ */
+ if (dlp->dl_primitive == DL_ERROR_ACK) {
+ report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d",
+ dlp->error_ack.dl_errno,
+ dlp->error_ack.dl_unix_errno);
+ goto out;
+ }
+ if (dlp->dl_primitive != DL_OK_ACK) {
+ report(LOG_ERR, "getether: attach: not OK or ERROR");
+ goto out;
+ }
+ } /* unit >= 0 */
+#endif /* DL_ATTACH_REQ */
+
+ /*
+ * Get the Ethernet address the same way the ARP module
+ * does when it is pushed onto a new stream (bind).
+ * One should instead be able just do a dl_info_req
+ * but many drivers do not supply the hardware address
+ * in the response to dl_info_req (they MUST supply it
+ * for dl_bind_ack because the ARP module requires it).
+ */
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ dlp = (union DL_primitives *) tmpbuf;
+ dlp->dl_primitive = DL_BIND_REQ;
+ dlp->bind_req.dl_sap = 0x8FF; /* XXX - Unused SAP */
+ cbuf.buf = tmpbuf;
+ cbuf.len = DL_BIND_REQ_SIZE;
+ if (putmsg(fd, &cbuf, NULL, 0) < 0) {
+ report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg());
+ goto out;
+ }
+ /* Recv the ack. */
+ cbuf.buf = tmpbuf;
+ cbuf.maxlen = sizeof(tmpbuf);
+ flags = 0;
+ if (getmsg(fd, &cbuf, NULL, &flags) < 0) {
+ report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg());
+ goto out;
+ }
+ /*
+ * Check the type, etc.
+ */
+ if (dlp->dl_primitive == DL_ERROR_ACK) {
+ report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d",
+ dlp->error_ack.dl_errno,
+ dlp->error_ack.dl_unix_errno);
+ goto out;
+ }
+ if (dlp->dl_primitive != DL_BIND_ACK) {
+ report(LOG_ERR, "getether: bind: not OK or ERROR");
+ goto out;
+ }
+ if (dlp->bind_ack.dl_addr_offset == 0) {
+ report(LOG_ERR, "getether: bind: ack has no address");
+ goto out;
+ }
+ if (dlp->bind_ack.dl_addr_length < EALEN) {
+ report(LOG_ERR, "getether: bind: ack address truncated");
+ goto out;
+ }
+ /*
+ * Copy the Ethernet address out of the message.
+ */
+ enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset;
+ memcpy(eap, enaddr, EALEN);
+ rc = 0;
+
+ out:
+ close(fd);
+ return rc;
+}
+
+#define GETETHER
+#endif /* SVR4 */
+
+
+#ifdef __linux__
+/*
+ * This is really easy on Linux! This version (for linux)
+ * written by Nigel Metheringham <nigelm@ohm.york.ac.uk> and
+ * updated by Pauline Middelink <middelin@polyware.iaf.nl>
+ *
+ * The code is almost identical to the Ultrix code - however
+ * the names are different to confuse the innocent :-)
+ * Most of this code was stolen from the Ultrix bit above.
+ */
+
+#include <memory.h>
+#include <sys/ioctl.h>
+#include <net/if.h> /* struct ifreq */
+#include <sys/socketio.h> /* Needed for IOCTL defs */
+
+int
+getether(ifname, eap)
+ char *ifname, *eap;
+{
+ int rc = -1;
+ int fd;
+ struct ifreq phys;
+
+ memset(&phys, 0, sizeof(phys));
+ strcpy(phys.ifr_name, ifname);
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ report(LOG_ERR, "getether: socket(INET,DGRAM) failed");
+ return -1;
+ }
+ if (ioctl(fd, SIOCGIFHWADDR, &phys) < 0) {
+ report(LOG_ERR, "getether: ioctl SIOCGIFHWADDR failed");
+ } else {
+ memcpy(eap, &phys.ifr_hwaddr.sa_data, EALEN);
+ rc = 0;
+ }
+ close(fd);
+ return rc;
+}
+
+#define GETETHER
+#endif /* __linux__ */
+
+
+/* If we don't know how on this system, just return an error. */
+#ifndef GETETHER
+int
+getether(ifname, eap)
+ char *ifname, *eap;
+{
+ return -1;
+}
+
+#endif /* !GETETHER */
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/getether.h b/libexec/bootpd/getether.h
new file mode 100644
index 000000000000..a68b1f8f12f5
--- /dev/null
+++ b/libexec/bootpd/getether.h
@@ -0,0 +1,3 @@
+/* getether.h */
+
+extern int getether(char *ifname, char *eaptr);
diff --git a/libexec/bootpd/getif.c b/libexec/bootpd/getif.c
new file mode 100644
index 000000000000..6d2ca48eaf42
--- /dev/null
+++ b/libexec/bootpd/getif.c
@@ -0,0 +1,142 @@
+/*
+ * getif.c : get an interface structure
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+#ifdef SVR4
+#include <sys/stropts.h>
+#endif
+
+#include <sys/time.h> /* for struct timeval in net/if.h */
+#include <net/if.h> /* for struct ifreq */
+#include <netinet/in.h>
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+#include <syslog.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "getif.h"
+#include "report.h"
+
+#ifdef __bsdi__
+#define BSD 43
+#endif
+
+static struct ifreq ifreq[10]; /* Holds interface configuration */
+static struct ifconf ifconf; /* points to ifreq */
+
+static int nmatch(u_char *ca, u_char *cb);
+
+/* Return a pointer to the interface struct for the passed address. */
+struct ifreq *
+getif(int s, struct in_addr *addrp)
+{
+ int maxmatch;
+ int len, m, incr;
+ struct ifreq *ifrq, *ifrmax;
+ struct sockaddr_in *sip;
+ char *p;
+
+ /* If no address was supplied, just return NULL. */
+ if (!addrp)
+ return (struct ifreq *) 0;
+
+ /* Get the interface config if not done already. */
+ if (ifconf.ifc_len == 0) {
+#ifdef SVR4
+ /*
+ * SysVr4 returns garbage if you do this the obvious way!
+ * This one took a while to figure out... -gwr
+ */
+ struct strioctl ioc;
+ ioc.ic_cmd = SIOCGIFCONF;
+ ioc.ic_timout = 0;
+ ioc.ic_len = sizeof(ifreq);
+ ioc.ic_dp = (char *) ifreq;
+ m = ioctl(s, I_STR, (char *) &ioc);
+ ifconf.ifc_len = ioc.ic_len;
+ ifconf.ifc_req = ifreq;
+#else /* SVR4 */
+ ifconf.ifc_len = sizeof(ifreq);
+ ifconf.ifc_req = ifreq;
+ m = ioctl(s, SIOCGIFCONF, (caddr_t) & ifconf);
+#endif /* SVR4 */
+ if ((m < 0) || (ifconf.ifc_len <= 0)) {
+ report(LOG_ERR, "ioctl SIOCGIFCONF");
+ return (struct ifreq *) 0;
+ }
+ }
+ maxmatch = 7; /* this many bits or less... */
+ ifrmax = (struct ifreq *) 0;/* ... is not a valid match */
+ p = (char *) ifreq;
+ len = ifconf.ifc_len;
+ while (len > 0) {
+ ifrq = (struct ifreq *) p;
+ sip = (struct sockaddr_in *) &ifrq->ifr_addr;
+ m = nmatch((u_char *)addrp, (u_char *)&(sip->sin_addr));
+ if (m > maxmatch) {
+ maxmatch = m;
+ ifrmax = ifrq;
+ }
+#ifndef IFNAMSIZ
+ /* BSD not defined or earlier than 4.3 */
+ incr = sizeof(*ifrq);
+#else
+ incr = ifrq->ifr_addr.sa_len + IFNAMSIZ;
+#endif
+
+ p += incr;
+ len -= incr;
+ }
+
+ return ifrmax;
+}
+
+/*
+ * Return the number of leading bits matching in the
+ * internet addresses supplied.
+ */
+static int
+nmatch(u_char *ca, u_char *cb)
+{
+ u_int m = 0; /* count of matching bits */
+ u_int n = 4; /* bytes left, then bitmask */
+
+ /* Count matching bytes. */
+ while (n && (*ca == *cb)) {
+ ca++;
+ cb++;
+ m += 8;
+ n--;
+ }
+ /* Now count matching bits. */
+ if (n) {
+ n = 0x80;
+ while (n && ((*ca & n) == (*cb & n))) {
+ m++;
+ n >>= 1;
+ }
+ }
+ return (m);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/getif.h b/libexec/bootpd/getif.h
new file mode 100644
index 000000000000..3199b6c151dc
--- /dev/null
+++ b/libexec/bootpd/getif.h
@@ -0,0 +1,3 @@
+/* getif.h */
+
+extern struct ifreq *getif(int, struct in_addr *);
diff --git a/libexec/bootpd/hash.c b/libexec/bootpd/hash.c
new file mode 100644
index 000000000000..735373c2f654
--- /dev/null
+++ b/libexec/bootpd/hash.c
@@ -0,0 +1,385 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+/*
+ * Generalized hash table ADT
+ *
+ * Provides multiple, dynamically-allocated, variable-sized hash tables on
+ * various data and keys.
+ *
+ * This package attempts to follow some of the coding conventions suggested
+ * by Bob Sidebotham and the AFS Clean Code Committee of the
+ * Information Technology Center at Carnegie Mellon.
+ */
+
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "hash.h"
+
+#define TRUE 1
+#define FALSE 0
+#ifndef NULL
+#define NULL 0
+#endif
+
+/*
+ * This can be changed to make internal routines visible to debuggers, etc.
+ */
+#ifndef PRIVATE
+#define PRIVATE static
+#endif
+
+PRIVATE void hashi_FreeMembers(hash_member *, hash_freefp);
+
+
+
+
+/*
+ * Hash table initialization routine.
+ *
+ * This routine creates and intializes a hash table of size "tablesize"
+ * entries. Successful calls return a pointer to the hash table (which must
+ * be passed to other hash routines to identify the hash table). Failed
+ * calls return NULL.
+ */
+
+hash_tbl *
+hash_Init(unsigned tablesize)
+{
+ hash_tbl *hashtblptr;
+ unsigned totalsize;
+
+ if (tablesize > 0) {
+ totalsize = sizeof(hash_tbl)
+ + sizeof(hash_member *) * (tablesize - 1);
+ hashtblptr = (hash_tbl *) malloc(totalsize);
+ if (hashtblptr) {
+ bzero((char *) hashtblptr, totalsize);
+ hashtblptr->size = tablesize; /* Success! */
+ hashtblptr->bucketnum = 0;
+ hashtblptr->member = (hashtblptr->table)[0];
+ }
+ } else {
+ hashtblptr = NULL; /* Disallow zero-length tables */
+ }
+ return hashtblptr; /* NULL if failure */
+}
+
+
+
+/*
+ * Frees an entire linked list of bucket members (used in the open
+ * hashing scheme). Does nothing if the passed pointer is NULL.
+ */
+
+PRIVATE void
+hashi_FreeMembers(hash_member *bucketptr, hash_freefp free_data)
+{
+ hash_member *nextbucket;
+ while (bucketptr) {
+ nextbucket = bucketptr->next;
+ (*free_data) (bucketptr->data);
+ free((char *) bucketptr);
+ bucketptr = nextbucket;
+ }
+}
+
+
+
+
+/*
+ * This routine re-initializes the hash table. It frees all the allocated
+ * memory and resets all bucket pointers to NULL.
+ */
+
+void
+hash_Reset(hash_tbl *hashtable, hash_freefp free_data)
+{
+ hash_member **bucketptr;
+ unsigned i;
+
+ bucketptr = hashtable->table;
+ for (i = 0; i < hashtable->size; i++) {
+ hashi_FreeMembers(*bucketptr, free_data);
+ *bucketptr++ = NULL;
+ }
+ hashtable->bucketnum = 0;
+ hashtable->member = (hashtable->table)[0];
+}
+
+
+
+/*
+ * Generic hash function to calculate a hash code from the given string.
+ *
+ * For each byte of the string, this function left-shifts the value in an
+ * accumulator and then adds the byte into the accumulator. The contents of
+ * the accumulator is returned after the entire string has been processed.
+ * It is assumed that this result will be used as the "hashcode" parameter in
+ * calls to other functions in this package. These functions automatically
+ * adjust the hashcode for the size of each hashtable.
+ *
+ * This algorithm probably works best when the hash table size is a prime
+ * number.
+ *
+ * Hopefully, this function is better than the previous one which returned
+ * the sum of the squares of all the bytes. I'm still open to other
+ * suggestions for a default hash function. The programmer is more than
+ * welcome to supply his/her own hash function as that is one of the design
+ * features of this package.
+ */
+
+unsigned
+hash_HashFunction(unsigned char *string, unsigned len)
+{
+ unsigned accum;
+
+ accum = 0;
+ for (; len > 0; len--) {
+ accum <<= 1;
+ accum += (unsigned) (*string++ & 0xFF);
+ }
+ return accum;
+}
+
+
+
+/*
+ * Returns TRUE if at least one entry for the given key exists; FALSE
+ * otherwise.
+ */
+
+int
+hash_Exists(hash_tbl *hashtable, unsigned hashcode, hash_cmpfp compare,
+ hash_datum *key)
+{
+ hash_member *memberptr;
+
+ memberptr = (hashtable->table)[hashcode % (hashtable->size)];
+ while (memberptr) {
+ if ((*compare) (key, memberptr->data)) {
+ return TRUE; /* Entry does exist */
+ }
+ memberptr = memberptr->next;
+ }
+ return FALSE; /* Entry does not exist */
+}
+
+
+
+/*
+ * Insert the data item "element" into the hash table using "hashcode"
+ * to determine the bucket number, and "compare" and "key" to determine
+ * its uniqueness.
+ *
+ * If the insertion is successful 0 is returned. If a matching entry
+ * already exists in the given bucket of the hash table, or some other error
+ * occurs, -1 is returned and the insertion is not done.
+ */
+
+int
+hash_Insert(hash_tbl *hashtable, unsigned hashcode, hash_cmpfp compare,
+ hash_datum *key, hash_datum *element)
+{
+ hash_member *temp;
+
+ hashcode %= hashtable->size;
+ if (hash_Exists(hashtable, hashcode, compare, key)) {
+ return -1; /* At least one entry already exists */
+ }
+ temp = (hash_member *) malloc(sizeof(hash_member));
+ if (!temp)
+ return -1; /* malloc failed! */
+
+ temp->data = element;
+ temp->next = (hashtable->table)[hashcode];
+ (hashtable->table)[hashcode] = temp;
+ return 0; /* Success */
+}
+
+
+
+/*
+ * Delete all data elements which match the given key. If at least one
+ * element is found and the deletion is successful, 0 is returned.
+ * If no matching elements can be found in the hash table, -1 is returned.
+ */
+
+int
+hash_Delete(hash_tbl *hashtable, unsigned hashcode, hash_cmpfp compare,
+ hash_datum *key, hash_freefp free_data)
+{
+ hash_member *memberptr, *tempptr;
+ hash_member *previous = NULL;
+ int retval;
+
+ retval = -1;
+ hashcode %= hashtable->size;
+
+ /*
+ * Delete the first member of the list if it matches. Since this moves
+ * the second member into the first position we have to keep doing this
+ * over and over until it no longer matches.
+ */
+ memberptr = (hashtable->table)[hashcode];
+ while (memberptr && (*compare) (key, memberptr->data)) {
+ (hashtable->table)[hashcode] = memberptr->next;
+ /*
+ * Stop hashi_FreeMembers() from deleting the whole list!
+ */
+ memberptr->next = NULL;
+ hashi_FreeMembers(memberptr, free_data);
+ memberptr = (hashtable->table)[hashcode];
+ retval = 0;
+ }
+
+ /*
+ * Now traverse the rest of the list
+ */
+ if (memberptr) {
+ previous = memberptr;
+ memberptr = memberptr->next;
+ }
+ while (memberptr) {
+ if ((*compare) (key, memberptr->data)) {
+ tempptr = memberptr;
+ previous->next = memberptr = memberptr->next;
+ /*
+ * Put the brakes on hashi_FreeMembers(). . . .
+ */
+ tempptr->next = NULL;
+ hashi_FreeMembers(tempptr, free_data);
+ retval = 0;
+ } else {
+ previous = memberptr;
+ memberptr = memberptr->next;
+ }
+ }
+ return retval;
+}
+
+
+
+/*
+ * Locate and return the data entry associated with the given key.
+ *
+ * If the data entry is found, a pointer to it is returned. Otherwise,
+ * NULL is returned.
+ */
+
+hash_datum *
+hash_Lookup(hash_tbl *hashtable, unsigned hashcode, hash_cmpfp compare,
+ hash_datum *key)
+{
+ hash_member *memberptr;
+
+ memberptr = (hashtable->table)[hashcode % (hashtable->size)];
+ while (memberptr) {
+ if ((*compare) (key, memberptr->data)) {
+ return (memberptr->data);
+ }
+ memberptr = memberptr->next;
+ }
+ return NULL;
+}
+
+
+
+/*
+ * Return the next available entry in the hashtable for a linear search
+ */
+
+hash_datum *
+hash_NextEntry(hash_tbl *hashtable)
+{
+ unsigned bucket;
+ hash_member *memberptr;
+
+ /*
+ * First try to pick up where we left off.
+ */
+ memberptr = hashtable->member;
+ if (memberptr) {
+ hashtable->member = memberptr->next; /* Set up for next call */
+ return memberptr->data; /* Return the data */
+ }
+ /*
+ * We hit the end of a chain, so look through the array of buckets
+ * until we find a new chain (non-empty bucket) or run out of buckets.
+ */
+ bucket = hashtable->bucketnum + 1;
+ while ((bucket < hashtable->size) &&
+ !(memberptr = (hashtable->table)[bucket])) {
+ bucket++;
+ }
+
+ /*
+ * Check to see if we ran out of buckets.
+ */
+ if (bucket >= hashtable->size) {
+ /*
+ * Reset to top of table for next call.
+ */
+ hashtable->bucketnum = 0;
+ hashtable->member = (hashtable->table)[0];
+ /*
+ * But return end-of-table indication to the caller this time.
+ */
+ return NULL;
+ }
+ /*
+ * Must have found a non-empty bucket.
+ */
+ hashtable->bucketnum = bucket;
+ hashtable->member = memberptr->next; /* Set up for next call */
+ return memberptr->data; /* Return the data */
+}
+
+
+
+/*
+ * Return the first entry in a hash table for a linear search
+ */
+
+hash_datum *
+hash_FirstEntry(hash_tbl *hashtable)
+{
+ hashtable->bucketnum = 0;
+ hashtable->member = (hashtable->table)[0];
+ return hash_NextEntry(hashtable);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/hash.h b/libexec/bootpd/hash.h
new file mode 100644
index 000000000000..14aefe969d0d
--- /dev/null
+++ b/libexec/bootpd/hash.h
@@ -0,0 +1,147 @@
+#ifndef HASH_H
+#define HASH_H
+/* hash.h */
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+************************************************************************/
+
+/*
+ * Generalized hash table ADT
+ *
+ * Provides multiple, dynamically-allocated, variable-sized hash tables on
+ * various data and keys.
+ *
+ * This package attempts to follow some of the coding conventions suggested
+ * by Bob Sidebotham and the AFS Clean Code Committee.
+ */
+
+
+/*
+ * The user must supply the following:
+ *
+ * 1. A comparison function which is declared as:
+ *
+ * int compare(data1, data2)
+ * hash_datum *data1, *data2;
+ *
+ * This function must compare the desired fields of data1 and
+ * data2 and return TRUE (1) if the data should be considered
+ * equivalent (i.e. have the same key value) or FALSE (0)
+ * otherwise. This function is called through a pointer passed to
+ * the various hashtable functions (thus pointers to different
+ * functions may be passed to effect different tests on different
+ * hash tables).
+ *
+ * Internally, all the functions of this package always call the
+ * compare function with the "key" parameter as the first parameter,
+ * and a full data element as the second parameter. Thus, the key
+ * and element arguments to functions such as hash_Lookup() may
+ * actually be of different types and the programmer may provide a
+ * compare function which compares the two different object types
+ * as desired.
+ *
+ * Example:
+ *
+ * int compare(key, element)
+ * char *key;
+ * struct some_complex_structure *element;
+ * {
+ * return !strcmp(key, element->name);
+ * }
+ *
+ * key = "John C. Doe"
+ * element = &some_complex_structure
+ * hash_Lookup(table, hashcode, compare, key);
+ *
+ * 2. A hash function yielding an unsigned integer value to be used
+ * as the hashcode (index into the hashtable). Thus, the user
+ * may hash on whatever data is desired and may use several
+ * different hash functions for various different hash tables.
+ * The actual hash table index will be the passed hashcode modulo
+ * the hash table size.
+ *
+ * A generalized hash function, hash_HashFunction(), is included
+ * with this package to make things a little easier. It is not
+ * guaranteed to use the best hash algorithm in existence. . . .
+ */
+
+
+
+/*
+ * Various hash table definitions
+ */
+
+
+/*
+ * Define "hash_datum" as a universal data type
+ */
+typedef void hash_datum;
+
+typedef struct hash_memberstruct hash_member;
+typedef struct hash_tblstruct hash_tbl;
+typedef struct hash_tblstruct_hdr hash_tblhdr;
+
+struct hash_memberstruct {
+ hash_member *next;
+ hash_datum *data;
+};
+
+struct hash_tblstruct_hdr {
+ unsigned size, bucketnum;
+ hash_member *member;
+};
+
+struct hash_tblstruct {
+ unsigned size, bucketnum;
+ hash_member *member; /* Used for linear dump */
+ hash_member *table[1]; /* Dynamically extended */
+};
+
+/* ANSI function prototypes or empty arg list? */
+
+typedef int (*hash_cmpfp)(hash_datum *, hash_datum *);
+typedef void (*hash_freefp)(hash_datum *);
+
+extern hash_tbl *hash_Init(u_int tablesize);
+
+extern void hash_Reset(hash_tbl *tbl, hash_freefp);
+
+extern unsigned hash_HashFunction(u_char *str, u_int len);
+
+extern int hash_Exists(hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key);
+
+extern int hash_Insert(hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key,
+ hash_datum *element);
+
+extern int hash_Delete(hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key,
+ hash_freefp);
+
+extern hash_datum *hash_Lookup(hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key);
+
+extern hash_datum *hash_FirstEntry(hash_tbl *);
+
+extern hash_datum *hash_NextEntry(hash_tbl *);
+
+#endif /* HASH_H */
diff --git a/libexec/bootpd/hwaddr.c b/libexec/bootpd/hwaddr.c
new file mode 100644
index 000000000000..4bac34957545
--- /dev/null
+++ b/libexec/bootpd/hwaddr.c
@@ -0,0 +1,333 @@
+/*
+ * hwaddr.c - routines that deal with hardware addresses.
+ * (i.e. Ethernet)
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+#ifdef SVR4
+#include <sys/stream.h>
+#include <stropts.h>
+#include <fcntl.h>
+#endif
+
+#ifdef _AIX32
+#include <sys/time.h> /* for struct timeval in net/if.h */
+#include <net/if.h> /* for struct ifnet in net/if_arp.h */
+#endif
+
+#include <net/if_arp.h>
+#include <netinet/in.h>
+
+#ifdef WIN_TCP
+#include <netinet/if_ether.h>
+#include <sys/dlpi.h>
+#endif
+
+#include <stdio.h>
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+#include <syslog.h>
+
+#ifndef ATF_INUSE /* Not defined on some systems (i.e. Linux) */
+#define ATF_INUSE 0
+#endif
+
+/* For BSD 4.4, set arp entry by writing to routing socket */
+#if defined(BSD)
+#if BSD >= 199306
+extern int bsd_arp_set(struct in_addr *, char *, int);
+#endif
+#endif
+
+#include "bptypes.h"
+#include "hwaddr.h"
+#include "report.h"
+
+extern int debug;
+
+/*
+ * Hardware address lengths (in bytes) and network name based on hardware
+ * type code. List in order specified by Assigned Numbers RFC; Array index
+ * is hardware type code. Entries marked as zero are unknown to the author
+ * at this time. . . .
+ */
+
+struct hwinfo hwinfolist[] =
+{
+ {0, "Reserved"}, /* Type 0: Reserved (don't use this) */
+ {6, "Ethernet"}, /* Type 1: 10Mb Ethernet (48 bits) */
+ {1, "3Mb Ethernet"}, /* Type 2: 3Mb Ethernet (8 bits) */
+ {0, "AX.25"}, /* Type 3: Amateur Radio AX.25 */
+ {1, "ProNET"}, /* Type 4: Proteon ProNET Token Ring */
+ {0, "Chaos"}, /* Type 5: Chaos */
+ {6, "IEEE 802"}, /* Type 6: IEEE 802 Networks */
+ {0, "ARCNET"} /* Type 7: ARCNET */
+};
+int hwinfocnt = sizeof(hwinfolist) / sizeof(hwinfolist[0]);
+
+
+/*
+ * Setup the arp cache so that IP address 'ia' will be temporarily
+ * bound to hardware address 'ha' of length 'len'.
+ */
+void
+setarp(int s, struct in_addr *ia, int hafamily, u_char *haddr, int halen)
+{
+#ifdef SIOCSARP
+#ifdef WIN_TCP
+ /* This is an SVR4 with different networking code from
+ * Wollongong WIN-TCP. Not quite like the Lachman code.
+ * Code from: drew@drewsun.FEITH.COM (Andrew B. Sudell)
+ */
+#undef SIOCSARP
+#define SIOCSARP ARP_ADD
+ struct arptab arpreq; /* Arp table entry */
+
+ bzero((caddr_t) &arpreq, sizeof(arpreq));
+ arpreq.at_flags = ATF_COM;
+
+ /* Set up IP address */
+ arpreq.at_in = ia->s_addr;
+
+ /* Set up Hardware Address */
+ bcopy(haddr, arpreq.at_enaddr, halen);
+
+ /* Set the Date Link type. */
+ /* XXX - Translate (hafamily) to dltype somehow? */
+ arpreq.at_dltype = DL_ETHER;
+
+#else /* WIN_TCP */
+ /* Good old Berkeley way. */
+ struct arpreq arpreq; /* Arp request ioctl block */
+ struct sockaddr_in *si;
+ char *p;
+
+ bzero((caddr_t) &arpreq, sizeof(arpreq));
+ arpreq.arp_flags = ATF_INUSE | ATF_COM;
+
+ /* Set up the protocol address. */
+ arpreq.arp_pa.sa_family = AF_INET;
+ si = (struct sockaddr_in *) &arpreq.arp_pa;
+ si->sin_addr = *ia;
+
+ /* Set up the hardware address. */
+#ifdef __linux__ /* XXX - Do others need this? -gwr */
+ /*
+ * Linux requires the sa_family field set.
+ * longyear@netcom.com (Al Longyear)
+ */
+ arpreq.arp_ha.sa_family = hafamily;
+#endif /* linux */
+
+ /* This variable is just to help catch type mismatches. */
+ p = arpreq.arp_ha.sa_data;
+ bcopy(haddr, p, halen);
+#endif /* WIN_TCP */
+
+#ifdef SVR4
+ /*
+ * And now the stuff for System V Rel 4.x which does not
+ * appear to allow SIOCxxx ioctls on a socket descriptor.
+ * Thanks to several people: (all sent the same fix)
+ * Barney Wolff <barney@databus.com>,
+ * bear@upsys.se (Bj|rn Sj|holm),
+ * Michael Kuschke <Michael.Kuschke@Materna.DE>,
+ */
+ {
+ int fd;
+ struct strioctl iocb;
+
+ if ((fd=open("/dev/arp", O_RDWR)) < 0) {
+ report(LOG_ERR, "open /dev/arp: %s\n", get_errmsg());
+ }
+ iocb.ic_cmd = SIOCSARP;
+ iocb.ic_timout = 0;
+ iocb.ic_dp = (char *)&arpreq;
+ iocb.ic_len = sizeof(arpreq);
+ if (ioctl(fd, I_STR, (caddr_t)&iocb) < 0) {
+ report(LOG_ERR, "ioctl I_STR: %s\n", get_errmsg());
+ }
+ close (fd);
+ }
+#else /* SVR4 */
+ /*
+ * On SunOS, the ioctl sometimes returns ENXIO, and it
+ * appears to happen when the ARP cache entry you tried
+ * to add is already in the cache. (Sigh...)
+ * XXX - Should this error simply be ignored? -gwr
+ */
+ if (ioctl(s, SIOCSARP, (caddr_t) &arpreq) < 0) {
+ report(LOG_ERR, "ioctl SIOCSARP: %s", get_errmsg());
+ }
+#endif /* SVR4 */
+#else /* SIOCSARP */
+#if defined(BSD) && (BSD >= 199306)
+ bsd_arp_set(ia, haddr, halen);
+#else
+ /*
+ * Oh well, SIOCSARP is not defined. Just run arp(8).
+ * Need to delete partial entry first on some systems.
+ * XXX - Gag!
+ */
+ int status;
+ char buf[256];
+ char *a;
+ extern char *inet_ntoa();
+
+ a = inet_ntoa(*ia);
+ snprintf(buf, sizeof(buf), "arp -d %s; arp -s %s %s temp",
+ a, a, haddrtoa(haddr, halen));
+ if (debug > 2)
+ report(LOG_INFO, "%s", buf);
+ status = system(buf);
+ if (status)
+ report(LOG_ERR, "arp failed, exit code=0x%x", status);
+ return;
+#endif /* ! 4.4 BSD */
+#endif /* SIOCSARP */
+}
+
+
+/*
+ * Convert a hardware address to an ASCII string.
+ */
+char *
+haddrtoa(u_char *haddr, int hlen)
+{
+ static char haddrbuf[3 * MAXHADDRLEN + 1];
+ char *bufptr;
+
+ if (hlen > MAXHADDRLEN)
+ hlen = MAXHADDRLEN;
+
+ bufptr = haddrbuf;
+ while (hlen > 0) {
+ sprintf(bufptr, "%02X:", (unsigned) (*haddr++ & 0xFF));
+ bufptr += 3;
+ hlen--;
+ }
+ bufptr[-1] = 0;
+ return (haddrbuf);
+}
+
+
+/*
+ * haddr_conv802()
+ * --------------
+ *
+ * Converts a backwards address to a canonical address and a canonical address
+ * to a backwards address.
+ *
+ * INPUTS:
+ * adr_in - pointer to six byte string to convert (unsigned char *)
+ * addr_len - how many bytes to convert
+ *
+ * OUTPUTS:
+ * addr_out - The string is updated to contain the converted address.
+ *
+ * CALLER:
+ * many
+ *
+ * DATA:
+ * Uses conv802table to bit-reverse the address bytes.
+ */
+
+static u_char conv802table[256] =
+{
+ /* 0x00 */ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ /* 0x08 */ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ /* 0x10 */ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ /* 0x18 */ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ /* 0x20 */ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ /* 0x28 */ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ /* 0x30 */ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ /* 0x38 */ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ /* 0x40 */ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ /* 0x48 */ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ /* 0x50 */ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ /* 0x58 */ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ /* 0x60 */ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ /* 0x68 */ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ /* 0x70 */ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ /* 0x78 */ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ /* 0x80 */ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+ /* 0x88 */ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ /* 0x90 */ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+ /* 0x98 */ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ /* 0xA0 */ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ /* 0xA8 */ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ /* 0xB0 */ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+ /* 0xB8 */ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ /* 0xC0 */ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+ /* 0xC8 */ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ /* 0xD0 */ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ /* 0xD8 */ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ /* 0xE0 */ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+ /* 0xE8 */ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ /* 0xF0 */ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+ /* 0xF8 */ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
+};
+
+void
+haddr_conv802(u_char *addr_in, u_char *addr_out, int len)
+{
+ u_char *lim;
+
+ lim = addr_out + len;
+ while (addr_out < lim)
+ *addr_out++ = conv802table[*addr_in++];
+}
+
+#if 0
+/*
+ * For the record, here is a program to generate the
+ * bit-reverse table above.
+ */
+static int
+bitrev(int n)
+{
+ int i, r;
+
+ r = 0;
+ for (i = 0; i < 8; i++) {
+ r <<= 1;
+ r |= (n & 1);
+ n >>= 1;
+ }
+ return r;
+}
+
+void
+main(void)
+{
+ int i;
+ for (i = 0; i <= 0xFF; i++) {
+ if ((i & 7) == 0)
+ printf("/* 0x%02X */", i);
+ printf(" 0x%02X,", bitrev(i));
+ if ((i & 7) == 7)
+ printf("\n");
+ }
+}
+
+#endif
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/hwaddr.h b/libexec/bootpd/hwaddr.h
new file mode 100644
index 000000000000..8381666eea49
--- /dev/null
+++ b/libexec/bootpd/hwaddr.h
@@ -0,0 +1,34 @@
+/*
+ * hwaddr.h
+ */
+
+#ifndef HWADDR_H
+#define HWADDR_H
+
+#define MAXHADDRLEN 8 /* Max hw address length in bytes */
+
+/*
+ * This structure holds information about a specific network type. The
+ * length of the network hardware address is stored in "hlen".
+ * The string pointed to by "name" is the cononical name of the network.
+ */
+struct hwinfo {
+ unsigned int hlen;
+ char *name;
+};
+
+extern struct hwinfo hwinfolist[];
+extern int hwinfocnt;
+
+extern void setarp(int, struct in_addr *, int, u_char *, int);
+extern char *haddrtoa(u_char *, int);
+extern void haddr_conv802(u_char *, u_char *, int);
+
+/*
+ * Return the length in bytes of a hardware address of the given type.
+ * Return the canonical name of the network of the given type.
+ */
+#define haddrlength(type) ((hwinfolist[(int) (type)]).hlen)
+#define netname(type) ((hwinfolist[(int) (type)]).name)
+
+#endif /* HWADDR_H */
diff --git a/libexec/bootpd/lookup.c b/libexec/bootpd/lookup.c
new file mode 100644
index 000000000000..c520e7a9004c
--- /dev/null
+++ b/libexec/bootpd/lookup.c
@@ -0,0 +1,117 @@
+/*
+ * lookup.c - Lookup IP address, HW address, netmask
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <sys/time.h> /* for struct timeval in net/if.h */
+#include <net/if.h>
+#include <netinet/in.h>
+
+#ifdef ETC_ETHERS
+#include <net/ethernet.h>
+#endif
+
+#include <netdb.h>
+#include <strings.h>
+#include <syslog.h>
+
+#include "bootp.h"
+#include "lookup.h"
+#include "report.h"
+
+/*
+ * Lookup an Ethernet address and return it.
+ * Return NULL if addr not found.
+ */
+u_char *
+lookup_hwa(char *hostname, int htype)
+{
+ switch (htype) {
+
+ /* XXX - How is this done on other systems? -gwr */
+#ifdef ETC_ETHERS
+ case HTYPE_ETHERNET:
+ case HTYPE_IEEE802:
+ {
+ static struct ether_addr ea;
+ /* This does a lookup in /etc/ethers */
+ if (ether_hostton(hostname, &ea)) {
+ report(LOG_ERR, "no HW addr for host \"%s\"",
+ hostname);
+ return (u_char *) 0;
+ }
+ return (u_char *) & ea;
+ }
+#endif /* ETC_ETHERS */
+
+ default:
+ report(LOG_ERR, "no lookup for HW addr type %d", htype);
+ } /* switch */
+
+ /* If the system can't do it, just return an error. */
+ return (u_char *) 0;
+}
+
+
+/*
+ * Lookup an IP address.
+ * Return non-zero on failure.
+ */
+int
+lookup_ipa(char *hostname, u_int32 *result)
+{
+ struct hostent *hp;
+ hp = gethostbyname(hostname);
+ if (!hp)
+ return -1;
+ bcopy(hp->h_addr, result, sizeof(*result));
+ return 0;
+}
+
+
+/*
+ * Lookup a netmask
+ * Return non-zero on failure.
+ *
+ * XXX - This is OK as a default, but to really make this automatic,
+ * we would need to get the subnet mask from the ether interface.
+ * If this is wrong, specify the correct value in the bootptab.
+ *
+ * Both arguments are in network order
+ */
+int
+lookup_netmask(u_int32 addr, u_int32 *result)
+{
+ int32 m, a;
+
+ a = ntohl(addr);
+ m = 0;
+
+ if (IN_CLASSA(a))
+ m = IN_CLASSA_NET;
+
+ if (IN_CLASSB(a))
+ m = IN_CLASSB_NET;
+
+ if (IN_CLASSC(a))
+ m = IN_CLASSC_NET;
+
+ if (!m)
+ return -1;
+ *result = htonl(m);
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/lookup.h b/libexec/bootpd/lookup.h
new file mode 100644
index 000000000000..3b1890967351
--- /dev/null
+++ b/libexec/bootpd/lookup.h
@@ -0,0 +1,7 @@
+/* lookup.h */
+
+#include "bptypes.h" /* for int32, u_int32 */
+
+extern u_char *lookup_hwa(char *hostname, int htype);
+extern int lookup_ipa(char *hostname, u_int32 *addr);
+extern int lookup_netmask(u_int32 addr, u_int32 *mask);
diff --git a/libexec/bootpd/patchlevel.h b/libexec/bootpd/patchlevel.h
new file mode 100644
index 000000000000..2cc0f4e0ae1f
--- /dev/null
+++ b/libexec/bootpd/patchlevel.h
@@ -0,0 +1,6 @@
+/*
+ * patchlevel.h
+ */
+
+#define VERSION "2.4"
+#define PATCHLEVEL 3
diff --git a/libexec/bootpd/readfile.c b/libexec/bootpd/readfile.c
new file mode 100644
index 000000000000..1d9ff2163395
--- /dev/null
+++ b/libexec/bootpd/readfile.c
@@ -0,0 +1,2035 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+/*
+ * bootpd configuration file reading code.
+ *
+ * The routines in this file deal with reading, interpreting, and storing
+ * the information found in the bootpd configuration file (usually
+ * /etc/bootptab).
+ */
+
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <syslog.h>
+
+#include "bootp.h"
+#include "hash.h"
+#include "hwaddr.h"
+#include "lookup.h"
+#include "readfile.h"
+#include "report.h"
+#include "tzone.h"
+#include "bootpd.h"
+
+#define HASHTABLESIZE 257 /* Hash table size (prime) */
+
+/* Non-standard hardware address type (see bootp.h) */
+#define HTYPE_DIRECT 0
+
+/* Error codes returned by eval_symbol: */
+#define SUCCESS 0
+#define E_END_OF_ENTRY (-1)
+#define E_SYNTAX_ERROR (-2)
+#define E_UNKNOWN_SYMBOL (-3)
+#define E_BAD_IPADDR (-4)
+#define E_BAD_HWADDR (-5)
+#define E_BAD_LONGWORD (-6)
+#define E_BAD_HWATYPE (-7)
+#define E_BAD_PATHNAME (-8)
+#define E_BAD_VALUE (-9)
+
+/* Tag idendities. */
+#define SYM_NULL 0
+#define SYM_BOOTFILE 1
+#define SYM_COOKIE_SERVER 2
+#define SYM_DOMAIN_SERVER 3
+#define SYM_GATEWAY 4
+#define SYM_HWADDR 5
+#define SYM_HOMEDIR 6
+#define SYM_HTYPE 7
+#define SYM_IMPRESS_SERVER 8
+#define SYM_IPADDR 9
+#define SYM_LOG_SERVER 10
+#define SYM_LPR_SERVER 11
+#define SYM_NAME_SERVER 12
+#define SYM_RLP_SERVER 13
+#define SYM_SUBNET_MASK 14
+#define SYM_TIME_OFFSET 15
+#define SYM_TIME_SERVER 16
+#define SYM_VENDOR_MAGIC 17
+#define SYM_SIMILAR_ENTRY 18
+#define SYM_NAME_SWITCH 19
+#define SYM_BOOTSIZE 20
+#define SYM_BOOT_SERVER 22
+#define SYM_TFTPDIR 23
+#define SYM_DUMP_FILE 24
+#define SYM_DOMAIN_NAME 25
+#define SYM_SWAP_SERVER 26
+#define SYM_ROOT_PATH 27
+#define SYM_EXTEN_FILE 28
+#define SYM_REPLY_ADDR 29
+#define SYM_NIS_DOMAIN 30 /* RFC 1533 */
+#define SYM_NIS_SERVER 31 /* RFC 1533 */
+#define SYM_NTP_SERVER 32 /* RFC 1533 */
+#define SYM_EXEC_FILE 33 /* YORK_EX_OPTION */
+#define SYM_MSG_SIZE 34
+#define SYM_MIN_WAIT 35
+/* XXX - Add new tags here */
+
+#define OP_ADDITION 1 /* Operations on tags */
+#define OP_DELETION 2
+#define OP_BOOLEAN 3
+
+#define MAXINADDRS 16 /* Max size of an IP address list */
+#define MAXBUFLEN 256 /* Max temp buffer space */
+#define MAXENTRYLEN 2048 /* Max size of an entire entry */
+
+
+
+/*
+ * Structure used to map a configuration-file symbol (such as "ds") to a
+ * unique integer.
+ */
+
+struct symbolmap {
+ char *symbol;
+ int symbolcode;
+};
+
+
+struct htypename {
+ char *name;
+ byte htype;
+};
+
+
+PRIVATE int nhosts; /* Number of hosts (/w hw or IP address) */
+PRIVATE int nentries; /* Total number of entries */
+PRIVATE int32 modtime = 0; /* Last modification time of bootptab */
+PRIVATE char *current_hostname; /* Name of the current entry. */
+PRIVATE char current_tagname[8];
+
+/*
+ * List of symbolic names used in the bootptab file. The order and actual
+ * values of the symbol codes (SYM_. . .) are unimportant, but they must
+ * all be unique.
+ */
+
+PRIVATE struct symbolmap symbol_list[] = {
+ {"bf", SYM_BOOTFILE},
+ {"bs", SYM_BOOTSIZE},
+ {"cs", SYM_COOKIE_SERVER},
+ {"df", SYM_DUMP_FILE},
+ {"dn", SYM_DOMAIN_NAME},
+ {"ds", SYM_DOMAIN_SERVER},
+ {"ef", SYM_EXTEN_FILE},
+ {"ex", SYM_EXEC_FILE}, /* YORK_EX_OPTION */
+ {"gw", SYM_GATEWAY},
+ {"ha", SYM_HWADDR},
+ {"hd", SYM_HOMEDIR},
+ {"hn", SYM_NAME_SWITCH},
+ {"ht", SYM_HTYPE},
+ {"im", SYM_IMPRESS_SERVER},
+ {"ip", SYM_IPADDR},
+ {"lg", SYM_LOG_SERVER},
+ {"lp", SYM_LPR_SERVER},
+ {"ms", SYM_MSG_SIZE},
+ {"mw", SYM_MIN_WAIT},
+ {"ns", SYM_NAME_SERVER},
+ {"nt", SYM_NTP_SERVER},
+ {"ra", SYM_REPLY_ADDR},
+ {"rl", SYM_RLP_SERVER},
+ {"rp", SYM_ROOT_PATH},
+ {"sa", SYM_BOOT_SERVER},
+ {"sm", SYM_SUBNET_MASK},
+ {"sw", SYM_SWAP_SERVER},
+ {"tc", SYM_SIMILAR_ENTRY},
+ {"td", SYM_TFTPDIR},
+ {"to", SYM_TIME_OFFSET},
+ {"ts", SYM_TIME_SERVER},
+ {"vm", SYM_VENDOR_MAGIC},
+ {"yd", SYM_NIS_DOMAIN},
+ {"ys", SYM_NIS_SERVER},
+ /* XXX - Add new tags here */
+};
+
+
+/*
+ * List of symbolic names for hardware types. Name translates into
+ * hardware type code listed with it. Names must begin with a letter
+ * and must be all lowercase. This is searched linearly, so put
+ * commonly-used entries near the beginning.
+ */
+
+PRIVATE struct htypename htnamemap[] = {
+ {"ethernet", HTYPE_ETHERNET},
+ {"ethernet3", HTYPE_EXP_ETHERNET},
+ {"ether", HTYPE_ETHERNET},
+ {"ether3", HTYPE_EXP_ETHERNET},
+ {"ieee802", HTYPE_IEEE802},
+ {"tr", HTYPE_IEEE802},
+ {"token-ring", HTYPE_IEEE802},
+ {"pronet", HTYPE_PRONET},
+ {"chaos", HTYPE_CHAOS},
+ {"arcnet", HTYPE_ARCNET},
+ {"ax.25", HTYPE_AX25},
+ {"direct", HTYPE_DIRECT},
+ {"serial", HTYPE_DIRECT},
+ {"slip", HTYPE_DIRECT},
+ {"ppp", HTYPE_DIRECT}
+};
+
+
+
+/*
+ * Externals and forward declarations.
+ */
+
+boolean nmcmp(hash_datum *, hash_datum *);
+
+PRIVATE void
+ adjust(char **);
+PRIVATE void
+ del_string(struct shared_string *);
+PRIVATE void
+ del_bindata(struct shared_bindata *);
+PRIVATE void
+ del_iplist(struct in_addr_list *);
+PRIVATE void
+ eat_whitespace(char **);
+PRIVATE int
+ eval_symbol(char **, struct host *);
+PRIVATE void
+ fill_defaults(struct host *, char **);
+PRIVATE void
+ free_host(hash_datum *);
+PRIVATE struct in_addr_list *
+ get_addresses(char **);
+PRIVATE struct shared_string *
+ get_shared_string(char **);
+PRIVATE char *
+ get_string(char **, char *, u_int *);
+PRIVATE u_int32
+ get_u_long(char **);
+PRIVATE boolean
+ goodname(char *);
+PRIVATE boolean
+ hwinscmp(hash_datum *, hash_datum *);
+PRIVATE int
+ interp_byte(char **, byte *);
+PRIVATE void
+ makelower(char *);
+PRIVATE boolean
+ nullcmp(hash_datum *, hash_datum *);
+PRIVATE int
+ process_entry(struct host *, char *);
+PRIVATE int
+ process_generic(char **, struct shared_bindata **, u_int);
+PRIVATE byte *
+ prs_haddr(char **, u_int);
+PRIVATE int
+ prs_inetaddr(char **, u_int32 *);
+PRIVATE void
+ read_entry(FILE *, char *, u_int *);
+PRIVATE char *
+ smalloc(u_int);
+
+
+/*
+ * Vendor magic cookies for CMU and RFC1048
+ */
+u_char vm_cmu[4] = VM_CMU;
+u_char vm_rfc1048[4] = VM_RFC1048;
+
+/*
+ * Main hash tables
+ */
+hash_tbl *hwhashtable;
+hash_tbl *iphashtable;
+hash_tbl *nmhashtable;
+
+/*
+ * Allocate hash tables for hardware address, ip address, and hostname
+ * (shared by bootpd and bootpef)
+ */
+void
+rdtab_init(void)
+{
+ hwhashtable = hash_Init(HASHTABLESIZE);
+ iphashtable = hash_Init(HASHTABLESIZE);
+ nmhashtable = hash_Init(HASHTABLESIZE);
+ if (!(hwhashtable && iphashtable && nmhashtable)) {
+ report(LOG_ERR, "Unable to allocate hash tables.");
+ exit(1);
+ }
+}
+
+
+/*
+ * Read bootptab database file. Avoid rereading the file if the
+ * write date hasn't changed since the last time we read it.
+ */
+
+void
+readtab(int force)
+{
+ struct host *hp;
+ FILE *fp;
+ struct stat st;
+ unsigned hashcode, buflen;
+ static char buffer[MAXENTRYLEN];
+
+ /*
+ * Check the last modification time.
+ */
+ if (stat(bootptab, &st) < 0) {
+ report(LOG_ERR, "stat on \"%s\": %s",
+ bootptab, get_errmsg());
+ return;
+ }
+#ifdef DEBUG
+ if (debug > 3) {
+ char timestr[28];
+ strcpy(timestr, ctime(&(st.st_mtime)));
+ /* zap the newline */
+ timestr[24] = '\0';
+ report(LOG_INFO, "bootptab mtime: %s",
+ timestr);
+ }
+#endif
+ if ((force == 0) &&
+ (st.st_mtime == modtime) &&
+ st.st_nlink) {
+ /*
+ * hasn't been modified or deleted yet.
+ */
+ return;
+ }
+ if (debug)
+ report(LOG_INFO, "reading %s\"%s\"",
+ (modtime != 0L) ? "new " : "",
+ bootptab);
+
+ /*
+ * Open bootptab file.
+ */
+ if ((fp = fopen(bootptab, "r")) == NULL) {
+ report(LOG_ERR, "error opening \"%s\": %s", bootptab, get_errmsg());
+ return;
+ }
+ /*
+ * Record file modification time.
+ */
+ if (fstat(fileno(fp), &st) < 0) {
+ report(LOG_ERR, "fstat: %s", get_errmsg());
+ fclose(fp);
+ return;
+ }
+ modtime = st.st_mtime;
+
+ /*
+ * Entirely erase all hash tables.
+ */
+ hash_Reset(hwhashtable, free_host);
+ hash_Reset(iphashtable, free_host);
+ hash_Reset(nmhashtable, free_host);
+
+ nhosts = 0;
+ nentries = 0;
+ while (TRUE) {
+ buflen = sizeof(buffer);
+ read_entry(fp, buffer, &buflen);
+ if (buflen == 0) { /* More entries? */
+ break;
+ }
+ hp = (struct host *) smalloc(sizeof(struct host));
+ bzero((char *) hp, sizeof(*hp));
+ /* the link count it zero */
+
+ /*
+ * Get individual info
+ */
+ if (process_entry(hp, buffer) < 0) {
+ hp->linkcount = 1;
+ free_host((hash_datum *) hp);
+ continue;
+ }
+ /*
+ * If this is not a dummy entry, and the IP or HW
+ * address is not yet set, try to get them here.
+ * Dummy entries have . as first char of name.
+ */
+ if (goodname(hp->hostname->string)) {
+ char *hn = hp->hostname->string;
+ u_int32 value;
+ if (hp->flags.iaddr == 0) {
+ if (lookup_ipa(hn, &value)) {
+ report(LOG_ERR, "can not get IP addr for %s", hn);
+ report(LOG_ERR, "(dummy names should start with '.')");
+ } else {
+ hp->iaddr.s_addr = value;
+ hp->flags.iaddr = TRUE;
+ }
+ }
+ /* Set default subnet mask. */
+ if (hp->flags.subnet_mask == 0) {
+ if (lookup_netmask(hp->iaddr.s_addr, &value)) {
+ report(LOG_ERR, "can not get netmask for %s", hn);
+ } else {
+ hp->subnet_mask.s_addr = value;
+ hp->flags.subnet_mask = TRUE;
+ }
+ }
+ }
+ if (hp->flags.iaddr) {
+ nhosts++;
+ }
+ /* Register by HW addr if known. */
+ if (hp->flags.htype && hp->flags.haddr) {
+ /* We will either insert it or free it. */
+ hp->linkcount++;
+ hashcode = hash_HashFunction(hp->haddr, haddrlength(hp->htype));
+ if (hash_Insert(hwhashtable, hashcode, hwinscmp, hp, hp) < 0) {
+ report(LOG_NOTICE, "duplicate %s address: %s",
+ netname(hp->htype),
+ haddrtoa(hp->haddr, haddrlength(hp->htype)));
+ free_host((hash_datum *) hp);
+ continue;
+ }
+ }
+ /* Register by IP addr if known. */
+ if (hp->flags.iaddr) {
+ hashcode = hash_HashFunction((u_char *) & (hp->iaddr.s_addr), 4);
+ if (hash_Insert(iphashtable, hashcode, nullcmp, hp, hp) < 0) {
+ report(LOG_ERR,
+ "hash_Insert() failed on IP address insertion");
+ } else {
+ /* Just inserted the host struct in a new hash list. */
+ hp->linkcount++;
+ }
+ }
+ /* Register by Name (always known) */
+ hashcode = hash_HashFunction((u_char *) hp->hostname->string,
+ strlen(hp->hostname->string));
+ if (hash_Insert(nmhashtable, hashcode, nullcmp,
+ hp->hostname->string, hp) < 0) {
+ report(LOG_ERR,
+ "hash_Insert() failed on insertion of hostname: \"%s\"",
+ hp->hostname->string);
+ } else {
+ /* Just inserted the host struct in a new hash list. */
+ hp->linkcount++;
+ }
+
+ nentries++;
+ }
+
+ fclose(fp);
+ if (debug)
+ report(LOG_INFO, "read %d entries (%d hosts) from \"%s\"",
+ nentries, nhosts, bootptab);
+ return;
+}
+
+
+
+/*
+ * Read an entire host entry from the file pointed to by "fp" and insert it
+ * into the memory pointed to by "buffer". Leading whitespace and comments
+ * starting with "#" are ignored (removed). Backslashes (\) always quote
+ * the next character except that newlines preceded by a backslash cause
+ * line-continuation onto the next line. The entry is terminated by a
+ * newline character which is not preceded by a backslash. Sequences
+ * surrounded by double quotes are taken literally (including newlines, but
+ * not backslashes).
+ *
+ * The "bufsiz" parameter points to an unsigned int which specifies the
+ * maximum permitted buffer size. Upon return, this value will be replaced
+ * with the actual length of the entry (not including the null terminator).
+ *
+ * This code is a little scary. . . . I don't like using gotos in C
+ * either, but I first wrote this as an FSM diagram and gotos seemed like
+ * the easiest way to implement it. Maybe later I'll clean it up.
+ */
+
+PRIVATE void
+read_entry(FILE *fp, char *buffer, unsigned *bufsiz)
+{
+ int c, length;
+
+ length = 0;
+
+ /*
+ * Eat whitespace, blank lines, and comment lines.
+ */
+ top:
+ c = fgetc(fp);
+ if (c < 0) {
+ goto done; /* Exit if end-of-file */
+ }
+ if (isspace(c)) {
+ goto top; /* Skip over whitespace */
+ }
+ if (c == '#') {
+ while (TRUE) { /* Eat comments after # */
+ c = fgetc(fp);
+ if (c < 0) {
+ goto done; /* Exit if end-of-file */
+ }
+ if (c == '\n') {
+ goto top; /* Try to read the next line */
+ }
+ }
+ }
+ ungetc(c, fp); /* Other character, push it back to reprocess it */
+
+
+ /*
+ * Now we're actually reading a data entry. Get each character and
+ * assemble it into the data buffer, processing special characters like
+ * double quotes (") and backslashes (\).
+ */
+
+ mainloop:
+ c = fgetc(fp);
+ switch (c) {
+ case EOF:
+ case '\n':
+ goto done; /* Exit on EOF or newline */
+ case '\\':
+ c = fgetc(fp); /* Backslash, read a new character */
+ if (c < 0) {
+ goto done; /* Exit on EOF */
+ }
+ *buffer++ = c; /* Store the literal character */
+ length++;
+ if (length < *bufsiz - 1) {
+ goto mainloop;
+ } else {
+ goto done;
+ }
+ case '"':
+ *buffer++ = '"'; /* Store double-quote */
+ length++;
+ if (length >= *bufsiz - 1) {
+ goto done;
+ }
+ while (TRUE) { /* Special quote processing loop */
+ c = fgetc(fp);
+ switch (c) {
+ case EOF:
+ goto done; /* Exit on EOF . . . */
+ case '"':
+ *buffer++ = '"';/* Store matching quote */
+ length++;
+ if (length < *bufsiz - 1) {
+ goto mainloop; /* And continue main loop */
+ } else {
+ goto done;
+ }
+ case '\\':
+ if ((c = fgetc(fp)) < 0) { /* Backslash */
+ goto done; /* EOF. . . .*/
+ }
+ /* FALLTHROUGH */
+ default:
+ *buffer++ = c; /* Other character, store it */
+ length++;
+ if (length >= *bufsiz - 1) {
+ goto done;
+ }
+ }
+ }
+ case ':':
+ *buffer++ = c; /* Store colons */
+ length++;
+ if (length >= *bufsiz - 1) {
+ goto done;
+ }
+ do { /* But remove whitespace after them */
+ c = fgetc(fp);
+ if ((c < 0) || (c == '\n')) {
+ goto done;
+ }
+ } while (isspace(c)); /* Skip whitespace */
+
+ if (c == '\\') { /* Backslash quotes next character */
+ c = fgetc(fp);
+ if (c < 0) {
+ goto done;
+ }
+ if (c == '\n') {
+ goto top; /* Backslash-newline continuation */
+ }
+ }
+ /* FALLTHROUGH if "other" character */
+ default:
+ *buffer++ = c; /* Store other characters */
+ length++;
+ if (length >= *bufsiz - 1) {
+ goto done;
+ }
+ }
+ goto mainloop; /* Keep going */
+
+ done:
+ *buffer = '\0'; /* Terminate string */
+ *bufsiz = length; /* Tell the caller its length */
+}
+
+
+
+/*
+ * Parse out all the various tags and parameters in the host entry pointed
+ * to by "src". Stuff all the data into the appropriate fields of the
+ * host structure pointed to by "host". If there is any problem with the
+ * entry, an error message is reported via report(), no further processing
+ * is done, and -1 is returned. Successful calls return 0.
+ *
+ * (Some errors probably shouldn't be so completely fatal. . . .)
+ */
+
+PRIVATE int
+process_entry(struct host *host, char *src)
+{
+ int retval;
+ char *msg;
+
+ if (!host || *src == '\0') {
+ return -1;
+ }
+ host->hostname = get_shared_string(&src);
+#if 0
+ /* Be more liberal for the benefit of dummy tag names. */
+ if (!goodname(host->hostname->string)) {
+ report(LOG_ERR, "bad hostname: \"%s\"", host->hostname->string);
+ del_string(host->hostname);
+ return -1;
+ }
+#endif
+ current_hostname = host->hostname->string;
+ adjust(&src);
+ while (TRUE) {
+ retval = eval_symbol(&src, host);
+ if (retval == SUCCESS) {
+ adjust(&src);
+ continue;
+ }
+ if (retval == E_END_OF_ENTRY) {
+ /* The default subnet mask is set in readtab() */
+ return 0;
+ }
+ /* Some kind of error. */
+ switch (retval) {
+ case E_SYNTAX_ERROR:
+ msg = "bad syntax";
+ break;
+ case E_UNKNOWN_SYMBOL:
+ msg = "unknown symbol";
+ break;
+ case E_BAD_IPADDR:
+ msg = "bad INET address";
+ break;
+ case E_BAD_HWADDR:
+ msg = "bad hardware address";
+ break;
+ case E_BAD_LONGWORD:
+ msg = "bad longword value";
+ break;
+ case E_BAD_HWATYPE:
+ msg = "bad HW address type";
+ break;
+ case E_BAD_PATHNAME:
+ msg = "bad pathname (need leading '/')";
+ break;
+ case E_BAD_VALUE:
+ msg = "bad value";
+ break;
+ default:
+ msg = "unknown error";
+ break;
+ } /* switch */
+ report(LOG_ERR, "in entry named \"%s\", symbol \"%s\": %s",
+ current_hostname, current_tagname, msg);
+ return -1;
+ }
+}
+
+
+/*
+ * Macros for use in the function below:
+ */
+
+/* Parse one INET address stored directly in MEMBER. */
+#define PARSE_IA1(MEMBER) do \
+{ \
+ if (optype == OP_BOOLEAN) \
+ return E_SYNTAX_ERROR; \
+ hp->flags.MEMBER = FALSE; \
+ if (optype == OP_ADDITION) { \
+ if (prs_inetaddr(symbol, &value) < 0) \
+ return E_BAD_IPADDR; \
+ hp->MEMBER.s_addr = value; \
+ hp->flags.MEMBER = TRUE; \
+ } \
+} while (0)
+
+/* Parse a list of INET addresses pointed to by MEMBER */
+#define PARSE_IAL(MEMBER) do \
+{ \
+ if (optype == OP_BOOLEAN) \
+ return E_SYNTAX_ERROR; \
+ if (hp->flags.MEMBER) { \
+ hp->flags.MEMBER = FALSE; \
+ assert(hp->MEMBER); \
+ del_iplist(hp->MEMBER); \
+ hp->MEMBER = NULL; \
+ } \
+ if (optype == OP_ADDITION) { \
+ hp->MEMBER = get_addresses(symbol); \
+ if (hp->MEMBER == NULL) \
+ return E_SYNTAX_ERROR; \
+ hp->flags.MEMBER = TRUE; \
+ } \
+} while (0)
+
+/* Parse a shared string pointed to by MEMBER */
+#define PARSE_STR(MEMBER) do \
+{ \
+ if (optype == OP_BOOLEAN) \
+ return E_SYNTAX_ERROR; \
+ if (hp->flags.MEMBER) { \
+ hp->flags.MEMBER = FALSE; \
+ assert(hp->MEMBER); \
+ del_string(hp->MEMBER); \
+ hp->MEMBER = NULL; \
+ } \
+ if (optype == OP_ADDITION) { \
+ hp->MEMBER = get_shared_string(symbol); \
+ if (hp->MEMBER == NULL) \
+ return E_SYNTAX_ERROR; \
+ hp->flags.MEMBER = TRUE; \
+ } \
+} while (0)
+
+/* Parse an unsigned integer value for MEMBER */
+#define PARSE_UINT(MEMBER) do \
+{ \
+ if (optype == OP_BOOLEAN) \
+ return E_SYNTAX_ERROR; \
+ hp->flags.MEMBER = FALSE; \
+ if (optype == OP_ADDITION) { \
+ value = get_u_long(symbol); \
+ hp->MEMBER = value; \
+ hp->flags.MEMBER = TRUE; \
+ } \
+} while (0)
+
+/*
+ * Evaluate the two-character tag symbol pointed to by "symbol" and place
+ * the data in the structure pointed to by "hp". The pointer pointed to
+ * by "symbol" is updated to point past the source string (but may not
+ * point to the next tag entry).
+ *
+ * Obviously, this need a few more comments. . . .
+ */
+PRIVATE int
+eval_symbol(char **symbol, struct host *hp)
+{
+ char tmpstr[MAXSTRINGLEN];
+ byte *tmphaddr;
+ struct symbolmap *symbolptr;
+ u_int32 value;
+ int32 timeoff;
+ int i, numsymbols;
+ unsigned len;
+ int optype; /* Indicates boolean, addition, or deletion */
+
+ eat_whitespace(symbol);
+
+ /* Make sure this is set before returning. */
+ current_tagname[0] = (*symbol)[0];
+ current_tagname[1] = (*symbol)[1];
+ current_tagname[2] = 0;
+
+ if ((*symbol)[0] == '\0') {
+ return E_END_OF_ENTRY;
+ }
+ if ((*symbol)[0] == ':') {
+ return SUCCESS;
+ }
+ if ((*symbol)[0] == 'T') { /* generic symbol */
+ (*symbol)++;
+ value = get_u_long(symbol);
+ snprintf(current_tagname, sizeof(current_tagname),
+ "T%d", (int)value);
+ eat_whitespace(symbol);
+ if ((*symbol)[0] != '=') {
+ return E_SYNTAX_ERROR;
+ }
+ (*symbol)++;
+ if (!(hp->generic)) {
+ hp->generic = (struct shared_bindata *)
+ smalloc(sizeof(struct shared_bindata));
+ }
+ if (process_generic(symbol, &(hp->generic), (byte) (value & 0xFF)))
+ return E_SYNTAX_ERROR;
+ hp->flags.generic = TRUE;
+ return SUCCESS;
+ }
+ /*
+ * Determine the type of operation to be done on this symbol
+ */
+ switch ((*symbol)[2]) {
+ case '=':
+ optype = OP_ADDITION;
+ break;
+ case '@':
+ optype = OP_DELETION;
+ break;
+ case ':':
+ case '\0':
+ optype = OP_BOOLEAN;
+ break;
+ default:
+ return E_SYNTAX_ERROR;
+ }
+
+ symbolptr = symbol_list;
+ numsymbols = sizeof(symbol_list) / sizeof(struct symbolmap);
+ for (i = 0; i < numsymbols; i++) {
+ if (((symbolptr->symbol)[0] == (*symbol)[0]) &&
+ ((symbolptr->symbol)[1] == (*symbol)[1])) {
+ break;
+ }
+ symbolptr++;
+ }
+ if (i >= numsymbols) {
+ return E_UNKNOWN_SYMBOL;
+ }
+ /*
+ * Skip past the = or @ character (to point to the data) if this
+ * isn't a boolean operation. For boolean operations, just skip
+ * over the two-character tag symbol (and nothing else. . . .).
+ */
+ (*symbol) += (optype == OP_BOOLEAN) ? 2 : 3;
+
+ eat_whitespace(symbol);
+
+ /* The cases below are in order by symbolcode value. */
+ switch (symbolptr->symbolcode) {
+
+ case SYM_BOOTFILE:
+ PARSE_STR(bootfile);
+ break;
+
+ case SYM_COOKIE_SERVER:
+ PARSE_IAL(cookie_server);
+ break;
+
+ case SYM_DOMAIN_SERVER:
+ PARSE_IAL(domain_server);
+ break;
+
+ case SYM_GATEWAY:
+ PARSE_IAL(gateway);
+ break;
+
+ case SYM_HWADDR:
+ if (optype == OP_BOOLEAN)
+ return E_SYNTAX_ERROR;
+ hp->flags.haddr = FALSE;
+ if (optype == OP_ADDITION) {
+ /* Default the HW type to Ethernet */
+ if (hp->flags.htype == 0) {
+ hp->flags.htype = TRUE;
+ hp->htype = HTYPE_ETHERNET;
+ }
+ tmphaddr = prs_haddr(symbol, hp->htype);
+ if (!tmphaddr)
+ return E_BAD_HWADDR;
+ bcopy(tmphaddr, hp->haddr, haddrlength(hp->htype));
+ hp->flags.haddr = TRUE;
+ }
+ break;
+
+ case SYM_HOMEDIR:
+ PARSE_STR(homedir);
+ break;
+
+ case SYM_HTYPE:
+ if (optype == OP_BOOLEAN)
+ return E_SYNTAX_ERROR;
+ hp->flags.htype = FALSE;
+ if (optype == OP_ADDITION) {
+ value = 0L; /* Assume an illegal value */
+ eat_whitespace(symbol);
+ if (isdigit(**symbol)) {
+ value = get_u_long(symbol);
+ } else {
+ len = sizeof(tmpstr);
+ (void) get_string(symbol, tmpstr, &len);
+ makelower(tmpstr);
+ numsymbols = sizeof(htnamemap) /
+ sizeof(struct htypename);
+ for (i = 0; i < numsymbols; i++) {
+ if (!strcmp(htnamemap[i].name, tmpstr)) {
+ break;
+ }
+ }
+ if (i < numsymbols) {
+ value = htnamemap[i].htype;
+ }
+ }
+ if (value >= hwinfocnt) {
+ return E_BAD_HWATYPE;
+ }
+ hp->htype = (byte) (value & 0xFF);
+ hp->flags.htype = TRUE;
+ }
+ break;
+
+ case SYM_IMPRESS_SERVER:
+ PARSE_IAL(impress_server);
+ break;
+
+ case SYM_IPADDR:
+ PARSE_IA1(iaddr);
+ break;
+
+ case SYM_LOG_SERVER:
+ PARSE_IAL(log_server);
+ break;
+
+ case SYM_LPR_SERVER:
+ PARSE_IAL(lpr_server);
+ break;
+
+ case SYM_NAME_SERVER:
+ PARSE_IAL(name_server);
+ break;
+
+ case SYM_RLP_SERVER:
+ PARSE_IAL(rlp_server);
+ break;
+
+ case SYM_SUBNET_MASK:
+ PARSE_IA1(subnet_mask);
+ break;
+
+ case SYM_TIME_OFFSET:
+ if (optype == OP_BOOLEAN)
+ return E_SYNTAX_ERROR;
+ hp->flags.time_offset = FALSE;
+ if (optype == OP_ADDITION) {
+ len = sizeof(tmpstr);
+ (void) get_string(symbol, tmpstr, &len);
+ if (!strncmp(tmpstr, "auto", 4)) {
+ hp->time_offset = secondswest;
+ } else {
+ if (sscanf(tmpstr, "%d", (int*)&timeoff) != 1)
+ return E_BAD_LONGWORD;
+ hp->time_offset = timeoff;
+ }
+ hp->flags.time_offset = TRUE;
+ }
+ break;
+
+ case SYM_TIME_SERVER:
+ PARSE_IAL(time_server);
+ break;
+
+ case SYM_VENDOR_MAGIC:
+ if (optype == OP_BOOLEAN)
+ return E_SYNTAX_ERROR;
+ hp->flags.vm_cookie = FALSE;
+ if (optype == OP_ADDITION) {
+ if (strncmp(*symbol, "auto", 4)) {
+ /* The string is not "auto" */
+ if (!strncmp(*symbol, "rfc", 3)) {
+ bcopy(vm_rfc1048, hp->vm_cookie, 4);
+ } else if (!strncmp(*symbol, "cmu", 3)) {
+ bcopy(vm_cmu, hp->vm_cookie, 4);
+ } else {
+ if (!isdigit(**symbol))
+ return E_BAD_IPADDR;
+ if (prs_inetaddr(symbol, &value) < 0)
+ return E_BAD_IPADDR;
+ bcopy(&value, hp->vm_cookie, 4);
+ }
+ hp->flags.vm_cookie = TRUE;
+ }
+ }
+ break;
+
+ case SYM_SIMILAR_ENTRY:
+ switch (optype) {
+ case OP_ADDITION:
+ fill_defaults(hp, symbol);
+ break;
+ default:
+ return E_SYNTAX_ERROR;
+ }
+ break;
+
+ case SYM_NAME_SWITCH:
+ switch (optype) {
+ case OP_ADDITION:
+ return E_SYNTAX_ERROR;
+ case OP_DELETION:
+ hp->flags.send_name = FALSE;
+ hp->flags.name_switch = FALSE;
+ break;
+ case OP_BOOLEAN:
+ hp->flags.send_name = TRUE;
+ hp->flags.name_switch = TRUE;
+ break;
+ }
+ break;
+
+ case SYM_BOOTSIZE:
+ switch (optype) {
+ case OP_ADDITION:
+ if (!strncmp(*symbol, "auto", 4)) {
+ hp->flags.bootsize = TRUE;
+ hp->flags.bootsize_auto = TRUE;
+ } else {
+ hp->bootsize = (unsigned int) get_u_long(symbol);
+ hp->flags.bootsize = TRUE;
+ hp->flags.bootsize_auto = FALSE;
+ }
+ break;
+ case OP_DELETION:
+ hp->flags.bootsize = FALSE;
+ break;
+ case OP_BOOLEAN:
+ hp->flags.bootsize = TRUE;
+ hp->flags.bootsize_auto = TRUE;
+ break;
+ }
+ break;
+
+ case SYM_BOOT_SERVER:
+ PARSE_IA1(bootserver);
+ break;
+
+ case SYM_TFTPDIR:
+ PARSE_STR(tftpdir);
+ if ((hp->tftpdir != NULL) &&
+ (hp->tftpdir->string[0] != '/'))
+ return E_BAD_PATHNAME;
+ break;
+
+ case SYM_DUMP_FILE:
+ PARSE_STR(dump_file);
+ break;
+
+ case SYM_DOMAIN_NAME:
+ PARSE_STR(domain_name);
+ break;
+
+ case SYM_SWAP_SERVER:
+ PARSE_IA1(swap_server);
+ break;
+
+ case SYM_ROOT_PATH:
+ PARSE_STR(root_path);
+ break;
+
+ case SYM_EXTEN_FILE:
+ PARSE_STR(exten_file);
+ break;
+
+ case SYM_REPLY_ADDR:
+ PARSE_IA1(reply_addr);
+ break;
+
+ case SYM_NIS_DOMAIN:
+ PARSE_STR(nis_domain);
+ break;
+
+ case SYM_NIS_SERVER:
+ PARSE_IAL(nis_server);
+ break;
+
+ case SYM_NTP_SERVER:
+ PARSE_IAL(ntp_server);
+ break;
+
+#ifdef YORK_EX_OPTION
+ case SYM_EXEC_FILE:
+ PARSE_STR(exec_file);
+ break;
+#endif
+
+ case SYM_MSG_SIZE:
+ PARSE_UINT(msg_size);
+ if (hp->msg_size < BP_MINPKTSZ ||
+ hp->msg_size > MAX_MSG_SIZE)
+ return E_BAD_VALUE;
+ break;
+
+ case SYM_MIN_WAIT:
+ PARSE_UINT(min_wait);
+ break;
+
+ /* XXX - Add new tags here */
+
+ default:
+ return E_UNKNOWN_SYMBOL;
+
+ } /* switch symbolcode */
+
+ return SUCCESS;
+}
+#undef PARSE_IA1
+#undef PARSE_IAL
+#undef PARSE_STR
+
+
+
+
+/*
+ * Read a string from the buffer indirectly pointed to through "src" and
+ * move it into the buffer pointed to by "dest". A pointer to the maximum
+ * allowable length of the string (including null-terminator) is passed as
+ * "length". The actual length of the string which was read is returned in
+ * the unsigned integer pointed to by "length". This value is the same as
+ * that which would be returned by applying the strlen() function on the
+ * destination string (i.e the terminating null is not counted as a
+ * character). Trailing whitespace is removed from the string. For
+ * convenience, the function returns the new value of "dest".
+ *
+ * The string is read until the maximum number of characters, an unquoted
+ * colon (:), or a null character is read. The return string in "dest" is
+ * null-terminated.
+ */
+
+PRIVATE char *
+get_string(char **src, char *dest, unsigned *length)
+{
+ int n, len, quoteflag;
+
+ quoteflag = FALSE;
+ n = 0;
+ len = *length - 1;
+ while ((n < len) && (**src)) {
+ if (!quoteflag && (**src == ':')) {
+ break;
+ }
+ if (**src == '"') {
+ (*src)++;
+ quoteflag = !quoteflag;
+ continue;
+ }
+ if (**src == '\\') {
+ (*src)++;
+ if (!**src) {
+ break;
+ }
+ }
+ *dest++ = *(*src)++;
+ n++;
+ }
+
+ /*
+ * Remove that troublesome trailing whitespace. . .
+ */
+ while ((n > 0) && isspace(dest[-1])) {
+ dest--;
+ n--;
+ }
+
+ *dest = '\0';
+ *length = n;
+ return dest;
+}
+
+
+
+/*
+ * Read the string indirectly pointed to by "src", update the caller's
+ * pointer, and return a pointer to a malloc'ed shared_string structure
+ * containing the string.
+ *
+ * The string is read using the same rules as get_string() above.
+ */
+
+PRIVATE struct shared_string *
+get_shared_string(char **src)
+{
+ char retstring[MAXSTRINGLEN];
+ struct shared_string *s;
+ unsigned length;
+
+ length = sizeof(retstring);
+ (void) get_string(src, retstring, &length);
+
+ s = (struct shared_string *) smalloc(sizeof(struct shared_string)
+ + length);
+ s->linkcount = 1;
+ strcpy(s->string, retstring);
+
+ return s;
+}
+
+
+
+/*
+ * Load RFC1048 generic information directly into a memory buffer.
+ *
+ * "src" indirectly points to the ASCII representation of the generic data.
+ * "dest" points to a string structure which is updated to point to a new
+ * string with the new data appended to the old string. The old string is
+ * freed.
+ *
+ * The given tag value is inserted with the new data.
+ *
+ * The data may be represented as either a stream of hexadecimal numbers
+ * representing bytes (any or all bytes may optionally start with '0x' and
+ * be separated with periods ".") or as a quoted string of ASCII
+ * characters (the quotes are required).
+ */
+
+PRIVATE int
+process_generic(char **src, struct shared_bindata **dest, u_int tagvalue)
+{
+ byte tmpbuf[MAXBUFLEN];
+ byte *str;
+ struct shared_bindata *bdata;
+ u_int newlength, oldlength;
+
+ str = tmpbuf;
+ *str++ = (tagvalue & 0xFF); /* Store tag value */
+ str++; /* Skip over length field */
+ if ((*src)[0] == '"') { /* ASCII data */
+ newlength = sizeof(tmpbuf) - 2; /* Set maximum allowed length */
+ (void) get_string(src, (char *) str, &newlength);
+ newlength++; /* null terminator */
+ } else { /* Numeric data */
+ newlength = 0;
+ while (newlength < sizeof(tmpbuf) - 2) {
+ if (interp_byte(src, str++) < 0)
+ break;
+ newlength++;
+ if (**src == '.') {
+ (*src)++;
+ }
+ }
+ }
+ if ((*src)[0] != ':')
+ return -1;
+
+ tmpbuf[1] = (newlength & 0xFF);
+ oldlength = ((*dest)->length);
+ bdata = (struct shared_bindata *) smalloc(sizeof(struct shared_bindata)
+ + oldlength + newlength + 1);
+ if (oldlength > 0) {
+ bcopy((*dest)->data, bdata->data, oldlength);
+ }
+ bcopy(tmpbuf, bdata->data + oldlength, newlength + 2);
+ bdata->length = oldlength + newlength + 2;
+ bdata->linkcount = 1;
+ if (*dest) {
+ del_bindata(*dest);
+ }
+ *dest = bdata;
+ return 0;
+}
+
+
+
+/*
+ * Verify that the given string makes sense as a hostname (according to
+ * Appendix 1, page 29 of RFC882).
+ *
+ * Return TRUE for good names, FALSE otherwise.
+ */
+
+PRIVATE boolean
+goodname(char *hostname)
+{
+ do {
+ if (!isalpha(*hostname++)) { /* First character must be a letter */
+ return FALSE;
+ }
+ while (isalnum(*hostname) ||
+ (*hostname == '-') ||
+ (*hostname == '_') )
+ {
+ hostname++; /* Alphanumeric or a hyphen */
+ }
+ if (!isalnum(hostname[-1])) { /* Last must be alphanumeric */
+ return FALSE;
+ }
+ if (*hostname == '\0') {/* Done? */
+ return TRUE;
+ }
+ } while (*hostname++ == '.'); /* Dot, loop for next label */
+
+ return FALSE; /* If it's not a dot, lose */
+}
+
+
+
+/*
+ * Null compare function -- always returns FALSE so an element is always
+ * inserted into a hash table (i.e. there is never a collision with an
+ * existing element).
+ */
+
+PRIVATE boolean
+nullcmp(hash_datum *d1, hash_datum *d2)
+{
+ return FALSE;
+}
+
+
+/*
+ * Function for comparing a string with the hostname field of a host
+ * structure.
+ */
+
+boolean
+nmcmp(hash_datum *d1, hash_datum *d2)
+{
+ char *name = (char *) d1; /* XXX - OK? */
+ struct host *hp = (struct host *) d2;
+
+ return !strcmp(name, hp->hostname->string);
+}
+
+
+/*
+ * Compare function to determine whether two hardware addresses are
+ * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE
+ * otherwise.
+ *
+ * If the hardware addresses of "host1" and "host2" are identical, but
+ * they are on different IP subnets, this function returns FALSE.
+ *
+ * This function is used when inserting elements into the hardware address
+ * hash table.
+ */
+
+PRIVATE boolean
+hwinscmp(hash_datum *d1, hash_datum *d2)
+{
+ struct host *host1 = (struct host *) d1;
+ struct host *host2 = (struct host *) d2;
+
+ if (host1->htype != host2->htype) {
+ return FALSE;
+ }
+ if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) {
+ return FALSE;
+ }
+ /* XXX - Is the subnet_mask field set yet? */
+ if ((host1->subnet_mask.s_addr) == (host2->subnet_mask.s_addr)) {
+ if (((host1->iaddr.s_addr) & (host1->subnet_mask.s_addr)) !=
+ ((host2->iaddr.s_addr) & (host2->subnet_mask.s_addr)))
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+/*
+ * Macros for use in the function below:
+ */
+
+#define DUP_COPY(MEMBER) do \
+{ \
+ if (!hp->flags.MEMBER) { \
+ if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \
+ hp->MEMBER = hp2->MEMBER; \
+ } \
+ } \
+} while (0)
+
+#define DUP_LINK(MEMBER) do \
+{ \
+ if (!hp->flags.MEMBER) { \
+ if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \
+ assert(hp2->MEMBER); \
+ hp->MEMBER = hp2->MEMBER; \
+ (hp->MEMBER->linkcount)++; \
+ } \
+ } \
+} while (0)
+
+/*
+ * Process the "similar entry" symbol.
+ *
+ * The host specified as the value of the "tc" symbol is used as a template
+ * for the current host entry. Symbol values not explicitly set in the
+ * current host entry are inferred from the template entry.
+ */
+PRIVATE void
+fill_defaults(struct host *hp, char **src)
+{
+ unsigned int tlen, hashcode;
+ struct host *hp2;
+ char tstring[MAXSTRINGLEN];
+
+ tlen = sizeof(tstring);
+ (void) get_string(src, tstring, &tlen);
+ hashcode = hash_HashFunction((u_char *) tstring, tlen);
+ hp2 = (struct host *) hash_Lookup(nmhashtable, hashcode, nmcmp, tstring);
+
+ if (hp2 == NULL) {
+ report(LOG_ERR, "can't find tc=\"%s\"", tstring);
+ return;
+ }
+ DUP_LINK(bootfile);
+ DUP_LINK(cookie_server);
+ DUP_LINK(domain_server);
+ DUP_LINK(gateway);
+ /* haddr not copied */
+ DUP_LINK(homedir);
+ DUP_COPY(htype);
+
+ DUP_LINK(impress_server);
+ /* iaddr not copied */
+ DUP_LINK(log_server);
+ DUP_LINK(lpr_server);
+ DUP_LINK(name_server);
+ DUP_LINK(rlp_server);
+
+ DUP_COPY(subnet_mask);
+ DUP_COPY(time_offset);
+ DUP_LINK(time_server);
+
+ if (!hp->flags.vm_cookie) {
+ if ((hp->flags.vm_cookie = hp2->flags.vm_cookie)) {
+ bcopy(hp2->vm_cookie, hp->vm_cookie, 4);
+ }
+ }
+ if (!hp->flags.name_switch) {
+ if ((hp->flags.name_switch = hp2->flags.name_switch)) {
+ hp->flags.send_name = hp2->flags.send_name;
+ }
+ }
+ if (!hp->flags.bootsize) {
+ if ((hp->flags.bootsize = hp2->flags.bootsize)) {
+ hp->flags.bootsize_auto = hp2->flags.bootsize_auto;
+ hp->bootsize = hp2->bootsize;
+ }
+ }
+ DUP_COPY(bootserver);
+
+ DUP_LINK(tftpdir);
+ DUP_LINK(dump_file);
+ DUP_LINK(domain_name);
+
+ DUP_COPY(swap_server);
+ DUP_LINK(root_path);
+ DUP_LINK(exten_file);
+
+ DUP_COPY(reply_addr);
+
+ DUP_LINK(nis_domain);
+ DUP_LINK(nis_server);
+ DUP_LINK(ntp_server);
+
+#ifdef YORK_EX_OPTION
+ DUP_LINK(exec_file);
+#endif
+
+ DUP_COPY(msg_size);
+ DUP_COPY(min_wait);
+
+ /* XXX - Add new tags here */
+
+ DUP_LINK(generic);
+
+}
+#undef DUP_COPY
+#undef DUP_LINK
+
+
+
+/*
+ * This function adjusts the caller's pointer to point just past the
+ * first-encountered colon. If it runs into a null character, it leaves
+ * the pointer pointing to it.
+ */
+
+PRIVATE void
+adjust(char **s)
+{
+ char *t;
+
+ t = *s;
+ while (*t && (*t != ':')) {
+ t++;
+ }
+ if (*t) {
+ t++;
+ }
+ *s = t;
+}
+
+
+
+
+/*
+ * This function adjusts the caller's pointer to point to the first
+ * non-whitespace character. If it runs into a null character, it leaves
+ * the pointer pointing to it.
+ */
+
+PRIVATE void
+eat_whitespace(char **s)
+{
+ char *t;
+
+ t = *s;
+ while (*t && isspace(*t)) {
+ t++;
+ }
+ *s = t;
+}
+
+
+
+/*
+ * This function converts the given string to all lowercase.
+ */
+
+PRIVATE void
+makelower(char *s)
+{
+ while (*s) {
+ if (isupper(*s)) {
+ *s = tolower(*s);
+ }
+ s++;
+ }
+}
+
+
+
+/*
+ *
+ * N O T E :
+ *
+ * In many of the functions which follow, a parameter such as "src" or
+ * "symbol" is passed as a pointer to a pointer to something. This is
+ * done for the purpose of letting the called function update the
+ * caller's copy of the parameter (i.e. to effect call-by-reference
+ * parameter passing). The value of the actual parameter is only used
+ * to locate the real parameter of interest and then update this indirect
+ * parameter.
+ *
+ * I'm sure somebody out there won't like this. . . .
+ * (Yea, because it usually makes code slower... -gwr)
+ *
+ */
+
+
+
+/*
+ * "src" points to a character pointer which points to an ASCII string of
+ * whitespace-separated IP addresses. A pointer to an in_addr_list
+ * structure containing the list of addresses is returned. NULL is
+ * returned if no addresses were found at all. The pointer pointed to by
+ * "src" is updated to point to the first non-address (illegal) character.
+ */
+
+PRIVATE struct in_addr_list *
+get_addresses(char **src)
+{
+ struct in_addr tmpaddrlist[MAXINADDRS];
+ struct in_addr *address1, *address2;
+ struct in_addr_list *result;
+ unsigned addrcount, totalsize;
+
+ address1 = tmpaddrlist;
+ for (addrcount = 0; addrcount < MAXINADDRS; addrcount++) {
+ while (isspace(**src) || (**src == ',')) {
+ (*src)++;
+ }
+ if (!**src) { /* Quit if nothing more */
+ break;
+ }
+ if (prs_inetaddr(src, &(address1->s_addr)) < 0) {
+ break;
+ }
+ address1++; /* Point to next address slot */
+ }
+ if (addrcount < 1) {
+ result = NULL;
+ } else {
+ totalsize = sizeof(struct in_addr_list)
+ + (addrcount - 1) * sizeof(struct in_addr);
+ result = (struct in_addr_list *) smalloc(totalsize);
+ result->linkcount = 1;
+ result->addrcount = addrcount;
+ address1 = tmpaddrlist;
+ address2 = result->addr;
+ for (; addrcount > 0; addrcount--) {
+ address2->s_addr = address1->s_addr;
+ address1++;
+ address2++;
+ }
+ }
+ return result;
+}
+
+
+
+/*
+ * prs_inetaddr(src, result)
+ *
+ * "src" is a value-result parameter; the pointer it points to is updated
+ * to point to the next data position. "result" points to an unsigned long
+ * in which an address is returned.
+ *
+ * This function parses the IP address string in ASCII "dot notation" pointed
+ * to by (*src) and places the result (in network byte order) in the unsigned
+ * long pointed to by "result". For malformed addresses, -1 is returned,
+ * (*src) points to the first illegal character, and the unsigned long pointed
+ * to by "result" is unchanged. Successful calls return 0.
+ */
+
+PRIVATE int
+prs_inetaddr(char **src, u_int32 *result)
+{
+ char tmpstr[MAXSTRINGLEN];
+ u_int32 value;
+ u_int32 parts[4], *pp;
+ int n;
+ char *s, *t;
+
+ /* Leading alpha char causes IP addr lookup. */
+ if (isalpha(**src)) {
+ /* Lookup IP address. */
+ s = *src;
+ t = tmpstr;
+ while ((isalnum(*s) || (*s == '.') ||
+ (*s == '-') || (*s == '_') ) &&
+ (t < &tmpstr[MAXSTRINGLEN - 1]) )
+ *t++ = *s++;
+ *t = '\0';
+ *src = s;
+
+ n = lookup_ipa(tmpstr, result);
+ if (n < 0)
+ report(LOG_ERR, "can not get IP addr for %s", tmpstr);
+ return n;
+ }
+
+ /*
+ * Parse an address in Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16-bits)
+ * a.b (with b treated as 24 bits)
+ */
+ pp = parts;
+ loop:
+ /* If it's not a digit, return error. */
+ if (!isdigit(**src))
+ return -1;
+ *pp++ = get_u_long(src);
+ if (**src == '.') {
+ if (pp < (parts + 4)) {
+ (*src)++;
+ goto loop;
+ }
+ return (-1);
+ }
+#if 0
+ /* This is handled by the caller. */
+ if (**src && !(isspace(**src) || (**src == ':'))) {
+ return (-1);
+ }
+#endif
+
+ /*
+ * Construct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts;
+ switch (n) {
+ case 1: /* a -- 32 bits */
+ value = parts[0];
+ break;
+ case 2: /* a.b -- 8.24 bits */
+ value = (parts[0] << 24) | (parts[1] & 0xFFFFFF);
+ break;
+ case 3: /* a.b.c -- 8.8.16 bits */
+ value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) |
+ (parts[2] & 0xFFFF);
+ break;
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) |
+ ((parts[2] & 0xFF) << 8) | (parts[3] & 0xFF);
+ break;
+ default:
+ return (-1);
+ }
+ *result = htonl(value);
+ return (0);
+}
+
+
+
+/*
+ * "src" points to a pointer which in turn points to a hexadecimal ASCII
+ * string. This string is interpreted as a hardware address and returned
+ * as a pointer to the actual hardware address, represented as an array of
+ * bytes.
+ *
+ * The ASCII string must have the proper number of digits for the specified
+ * hardware type (e.g. twelve digits for a 48-bit Ethernet address).
+ * Two-digit sequences (bytes) may be separated with periods (.) and/or
+ * prefixed with '0x' for readability, but this is not required.
+ *
+ * For bad addresses, the pointer which "src" points to is updated to point
+ * to the start of the first two-digit sequence which was bad, and the
+ * function returns a NULL pointer.
+ */
+
+PRIVATE byte *
+prs_haddr(char **src, u_int htype)
+{
+ static byte haddr[MAXHADDRLEN];
+ byte *hap;
+ char tmpstr[MAXSTRINGLEN];
+ u_int tmplen;
+ unsigned hal;
+ char *p;
+
+ hal = haddrlength(htype); /* Get length of this address type */
+ if (hal <= 0) {
+ report(LOG_ERR, "Invalid addr type for HW addr parse");
+ return NULL;
+ }
+ tmplen = sizeof(tmpstr);
+ get_string(src, tmpstr, &tmplen);
+ p = tmpstr;
+
+ /* If it's a valid host name, try to lookup the HW address. */
+ if (goodname(p)) {
+ /* Lookup Hardware Address for hostname. */
+ if ((hap = lookup_hwa(p, htype)) != NULL)
+ return hap; /* success */
+ report(LOG_ERR, "Add 0x prefix if hex value starts with A-F");
+ /* OK, assume it must be numeric. */
+ }
+
+ hap = haddr;
+ while (hap < haddr + hal) {
+ if ((*p == '.') || (*p == ':'))
+ p++;
+ if (interp_byte(&p, hap++) < 0) {
+ return NULL;
+ }
+ }
+ return haddr;
+}
+
+
+
+/*
+ * "src" is a pointer to a character pointer which in turn points to a
+ * hexadecimal ASCII representation of a byte. This byte is read, the
+ * character pointer is updated, and the result is deposited into the
+ * byte pointed to by "retbyte".
+ *
+ * The usual '0x' notation is allowed but not required. The number must be
+ * a two digit hexadecimal number. If the number is invalid, "src" and
+ * "retbyte" are left untouched and -1 is returned as the function value.
+ * Successful calls return 0.
+ */
+
+PRIVATE int
+interp_byte(char **src, byte *retbyte)
+{
+ int v;
+
+ if ((*src)[0] == '0' &&
+ ((*src)[1] == 'x' ||
+ (*src)[1] == 'X')) {
+ (*src) += 2; /* allow 0x for hex, but don't require it */
+ }
+ if (!isxdigit((*src)[0]) || !isxdigit((*src)[1])) {
+ return -1;
+ }
+ if (sscanf(*src, "%2x", &v) != 1) {
+ return -1;
+ }
+ (*src) += 2;
+ *retbyte = (byte) (v & 0xFF);
+ return 0;
+}
+
+
+
+/*
+ * The parameter "src" points to a character pointer which points to an
+ * ASCII string representation of an unsigned number. The number is
+ * returned as an unsigned long and the character pointer is updated to
+ * point to the first illegal character.
+ */
+
+PRIVATE u_int32
+get_u_long(char **src)
+{
+ u_int32 value, base;
+ char c;
+
+ /*
+ * Collect number up to first illegal character. Values are specified
+ * as for C: 0x=hex, 0=octal, other=decimal.
+ */
+ value = 0;
+ base = 10;
+ if (**src == '0') {
+ base = 8;
+ (*src)++;
+ }
+ if (**src == 'x' || **src == 'X') {
+ base = 16;
+ (*src)++;
+ }
+ while ((c = **src)) {
+ if (isdigit(c)) {
+ value = (value * base) + (c - '0');
+ (*src)++;
+ continue;
+ }
+ if (base == 16 && isxdigit(c)) {
+ value = (value << 4) + ((c & ~32) + 10 - 'A');
+ (*src)++;
+ continue;
+ }
+ break;
+ }
+ return value;
+}
+
+
+
+/*
+ * Routines for deletion of data associated with the main data structure.
+ */
+
+
+/*
+ * Frees the entire host data structure given. Does nothing if the passed
+ * pointer is NULL.
+ */
+
+PRIVATE void
+free_host(hash_datum *hmp)
+{
+ struct host *hostptr = (struct host *) hmp;
+ if (hostptr == NULL)
+ return;
+ assert(hostptr->linkcount > 0);
+ if (--(hostptr->linkcount))
+ return; /* Still has references */
+ del_iplist(hostptr->cookie_server);
+ del_iplist(hostptr->domain_server);
+ del_iplist(hostptr->gateway);
+ del_iplist(hostptr->impress_server);
+ del_iplist(hostptr->log_server);
+ del_iplist(hostptr->lpr_server);
+ del_iplist(hostptr->name_server);
+ del_iplist(hostptr->rlp_server);
+ del_iplist(hostptr->time_server);
+ del_iplist(hostptr->nis_server);
+ del_iplist(hostptr->ntp_server);
+
+ /*
+ * XXX - Add new tags here
+ * (if the value is an IP list)
+ */
+
+ del_string(hostptr->hostname);
+ del_string(hostptr->homedir);
+ del_string(hostptr->bootfile);
+ del_string(hostptr->tftpdir);
+ del_string(hostptr->root_path);
+ del_string(hostptr->domain_name);
+ del_string(hostptr->dump_file);
+ del_string(hostptr->exten_file);
+ del_string(hostptr->nis_domain);
+
+#ifdef YORK_EX_OPTION
+ del_string(hostptr->exec_file);
+#endif
+
+ /*
+ * XXX - Add new tags here
+ * (if it is a shared string)
+ */
+
+ del_bindata(hostptr->generic);
+ free((char *) hostptr);
+}
+
+
+
+/*
+ * Decrements the linkcount on the given IP address data structure. If the
+ * linkcount goes to zero, the memory associated with the data is freed.
+ */
+
+PRIVATE void
+del_iplist(struct in_addr_list *iplist)
+{
+ if (iplist) {
+ if (!(--(iplist->linkcount))) {
+ free((char *) iplist);
+ }
+ }
+}
+
+
+
+/*
+ * Decrements the linkcount on a string data structure. If the count
+ * goes to zero, the memory associated with the string is freed. Does
+ * nothing if the passed pointer is NULL.
+ */
+
+PRIVATE void
+del_string(struct shared_string *stringptr)
+{
+ if (stringptr) {
+ if (!(--(stringptr->linkcount))) {
+ free((char *) stringptr);
+ }
+ }
+}
+
+
+
+/*
+ * Decrements the linkcount on a shared_bindata data structure. If the
+ * count goes to zero, the memory associated with the data is freed. Does
+ * nothing if the passed pointer is NULL.
+ */
+
+PRIVATE void
+del_bindata(struct shared_bindata *dataptr)
+{
+ if (dataptr) {
+ if (!(--(dataptr->linkcount))) {
+ free((char *) dataptr);
+ }
+ }
+}
+
+
+
+
+/* smalloc() -- safe malloc()
+ *
+ * Always returns a valid pointer (if it returns at all). The allocated
+ * memory is initialized to all zeros. If malloc() returns an error, a
+ * message is printed using the report() function and the program aborts
+ * with a status of 1.
+ */
+
+PRIVATE char *
+smalloc(unsigned nbytes)
+{
+ char *retvalue;
+
+ retvalue = malloc(nbytes);
+ if (!retvalue) {
+ report(LOG_ERR, "malloc() failure -- exiting");
+ exit(1);
+ }
+ bzero(retvalue, nbytes);
+ return retvalue;
+}
+
+
+/*
+ * Compare function to determine whether two hardware addresses are
+ * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE
+ * otherwise.
+ *
+ * This function is used when retrieving elements from the hardware address
+ * hash table.
+ */
+
+boolean
+hwlookcmp(hash_datum *d1, hash_datum *d2)
+{
+ struct host *host1 = (struct host *) d1;
+ struct host *host2 = (struct host *) d2;
+
+ if (host1->htype != host2->htype) {
+ return FALSE;
+ }
+ if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ * Compare function for doing IP address hash table lookup.
+ */
+
+boolean
+iplookcmp(hash_datum *d1, hash_datum *d2)
+{
+ struct host *host1 = (struct host *) d1;
+ struct host *host2 = (struct host *) d2;
+
+ return (host1->iaddr.s_addr == host2->iaddr.s_addr);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/readfile.h b/libexec/bootpd/readfile.h
new file mode 100644
index 000000000000..5da0c42af4a2
--- /dev/null
+++ b/libexec/bootpd/readfile.h
@@ -0,0 +1,10 @@
+/* readfile.h */
+
+#include "bptypes.h"
+#include "hash.h"
+
+extern boolean hwlookcmp(hash_datum *, hash_datum *);
+extern boolean iplookcmp(hash_datum *, hash_datum *);
+extern boolean nmcmp(hash_datum *, hash_datum *);
+extern void readtab(int);
+extern void rdtab_init(void);
diff --git a/libexec/bootpd/report.c b/libexec/bootpd/report.c
new file mode 100644
index 000000000000..eac6a100dfcb
--- /dev/null
+++ b/libexec/bootpd/report.c
@@ -0,0 +1,136 @@
+
+/*
+ * report() - calls syslog
+ */
+
+#include <stdarg.h>
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+
+#include "report.h"
+
+#ifndef LOG_NDELAY
+#define LOG_NDELAY 0
+#endif
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+#ifndef LOG_BOOTP
+#define LOG_BOOTP LOG_DAEMON
+#endif
+
+extern int debug;
+extern char *progname;
+
+/*
+ * This is initialized so you get stderr until you call
+ * report_init()
+ */
+static int stderr_only = 1;
+
+void
+report_init(int nolog)
+{
+ stderr_only = nolog;
+#ifdef SYSLOG
+ if (!stderr_only) {
+ openlog(progname, LOG_PID | LOG_NDELAY, LOG_BOOTP);
+ }
+#endif
+}
+
+/*
+ * This routine reports errors and such via stderr and syslog() if
+ * appopriate. It just helps avoid a lot of "#ifdef SYSLOG" constructs
+ * from being scattered throughout the code.
+ *
+ * The syntax is identical to syslog(3), but %m is not considered special
+ * for output to stderr (i.e. you'll see "%m" in the output. . .). Also,
+ * control strings should normally end with \n since newlines aren't
+ * automatically generated for stderr output (whereas syslog strips out all
+ * newlines and adds its own at the end).
+ */
+
+static char *levelnames[] = {
+#ifdef LOG_SALERT
+ "level(0): ",
+ "alert(1): ",
+ "alert(2): ",
+ "emerg(3): ",
+ "error(4): ",
+ "crit(5): ",
+ "warn(6): ",
+ "note(7): ",
+ "info(8): ",
+ "debug(9): ",
+ "level(?): "
+#else
+ "emerg(0): ",
+ "alert(1): ",
+ "crit(2): ",
+ "error(3): ",
+ "warn(4): ",
+ "note(5): ",
+ "info(6): ",
+ "debug(7): ",
+ "level(?): "
+#endif
+};
+static int numlevels = sizeof(levelnames) / sizeof(levelnames[0]);
+
+
+/*
+ * Print a log message using syslog(3) and/or stderr.
+ * The message passed in should not include a newline.
+ */
+void
+report(int priority, const char *fmt,...)
+{
+ va_list ap;
+ static char buf[128];
+
+ if ((priority < 0) || (priority >= numlevels)) {
+ priority = numlevels - 1;
+ }
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ /*
+ * Print the message
+ */
+ if (stderr_only || (debug > 2)) {
+ fprintf(stderr, "%s: %s %s\n",
+ progname, levelnames[priority], buf);
+ }
+#ifdef SYSLOG
+ if (!stderr_only)
+ syslog((priority | LOG_BOOTP), "%s", buf);
+#endif
+}
+
+
+
+/*
+ * Return pointer to static string which gives full filesystem error message.
+ */
+const char *
+get_errmsg(void)
+{
+ return strerror(errno);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/report.h b/libexec/bootpd/report.h
new file mode 100644
index 000000000000..a51e5f27f1f1
--- /dev/null
+++ b/libexec/bootpd/report.h
@@ -0,0 +1,5 @@
+/* report.h */
+
+extern void report_init(int nolog);
+extern void report(int, const char *, ...) __printflike(2, 3);
+extern const char *get_errmsg(void);
diff --git a/libexec/bootpd/rtmsg.c b/libexec/bootpd/rtmsg.c
new file mode 100644
index 000000000000..f2dbaf6eae33
--- /dev/null
+++ b/libexec/bootpd/rtmsg.c
@@ -0,0 +1,236 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1984, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1994
+ * Geoffrey M. Rehmet, All rights reserved.
+ *
+ * This code is derived from software which forms part of the 4.4-Lite
+ * Berkeley software distribution, which was in derived from software
+ * contributed to Berkeley by Sun Microsystems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * from arp.c 8.2 (Berkeley) 1/2/94
+ */
+
+#include <sys/param.h>
+/*
+ * Verify that we are at least 4.4 BSD
+ */
+#if defined(BSD)
+#if BSD >= 199306
+
+#include <sys/socket.h>
+#include <sys/filio.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "report.h"
+
+
+static int rtmsg(int);
+
+static int s = -1; /* routing socket */
+
+
+/*
+ * Open the routing socket
+ */
+static void getsocket () {
+ if (s < 0) {
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ report(LOG_ERR, "socket %s", strerror(errno));
+ exit(1);
+ }
+ } else {
+ /*
+ * Drain the socket of any unwanted routing messages.
+ */
+ int n;
+ char buf[512];
+
+ ioctl(s, FIONREAD, &n);
+ while (n > 0) {
+ read(s, buf, sizeof buf);
+ ioctl(s, FIONREAD, &n);
+ }
+ }
+}
+
+static struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
+static struct sockaddr_in blank_sin = {sizeof(blank_sin), AF_INET }, sin_m;
+static struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
+static int expire_time, flags, doing_proxy;
+static struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+/*
+ * Set an individual arp entry
+ */
+int
+bsd_arp_set(struct in_addr *ia, char *eaddr, int len)
+{
+ struct sockaddr_in *sin = &sin_m;
+ struct sockaddr_dl *sdl;
+ struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+ u_char *ea;
+ struct timespec tp;
+ int op = RTM_ADD;
+
+ getsocket();
+ sdl_m = blank_sdl;
+ sin_m = blank_sin;
+ sin->sin_addr = *ia;
+
+ ea = (u_char *)LLADDR(&sdl_m);
+ bcopy(eaddr, ea, len);
+ sdl_m.sdl_alen = len;
+ doing_proxy = flags = expire_time = 0;
+
+ /* make arp entry temporary */
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ expire_time = tp.tv_sec + 20 * 60;
+
+tryagain:
+ if (rtmsg(RTM_GET) < 0) {
+ report(LOG_WARNING, "rtmget: %s", strerror(errno));
+ return (1);
+ }
+ sin = (struct sockaddr_in *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin);
+ if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
+ if (sdl->sdl_family == AF_LINK &&
+ !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
+ case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
+ case IFT_ISO88024: case IFT_ISO88025:
+ op = RTM_CHANGE;
+ goto overwrite;
+ }
+ if (doing_proxy == 0) {
+ report(LOG_WARNING, "set: can only proxy for %s\n",
+ inet_ntoa(sin->sin_addr));
+ return (1);
+ }
+ goto tryagain;
+ }
+overwrite:
+ if (sdl->sdl_family != AF_LINK) {
+ report(LOG_WARNING,
+ "cannot intuit interface index and type for %s\n",
+ inet_ntoa(sin->sin_addr));
+ return (1);
+ }
+ sdl_m.sdl_type = sdl->sdl_type;
+ sdl_m.sdl_index = sdl->sdl_index;
+ return (rtmsg(op));
+}
+
+
+static int
+rtmsg(int cmd)
+{
+ static int seq;
+ int rlen;
+ struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ char *cp = m_rtmsg.m_space;
+ int l;
+
+ errno = 0;
+ bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+ rtm->rtm_flags = flags;
+ rtm->rtm_version = RTM_VERSION;
+
+ switch (cmd) {
+ default:
+ report(LOG_ERR, "set_arp: internal wrong cmd - exiting");
+ exit(1);
+ case RTM_ADD:
+ case RTM_CHANGE:
+ rtm->rtm_addrs |= RTA_GATEWAY;
+ rtm->rtm_rmx.rmx_expire = expire_time;
+ rtm->rtm_inits = RTV_EXPIRE;
+ rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
+ if (doing_proxy) {
+ rtm->rtm_addrs |= RTA_NETMASK;
+ rtm->rtm_flags &= ~RTF_HOST;
+ }
+ /* FALLTHROUGH */
+ case RTM_GET:
+ rtm->rtm_addrs |= RTA_DST;
+ }
+#define NEXTADDR(w, s) \
+ if (rtm->rtm_addrs & (w)) { \
+ bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
+
+ NEXTADDR(RTA_DST, sin_m);
+ NEXTADDR(RTA_GATEWAY, sdl_m);
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ rtm->rtm_msglen = cp - (char *)&m_rtmsg;
+
+ l = rtm->rtm_msglen;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_type = cmd;
+ if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
+ if ((errno != ESRCH) && !(errno == EEXIST && cmd == RTM_ADD)){
+ report(LOG_WARNING, "writing to routing socket: %s",
+ strerror(errno));
+ return (-1);
+ }
+ }
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || rtm->rtm_pid != getpid()));
+ if (l < 0)
+ report(LOG_WARNING, "arp: read from routing socket: %s\n",
+ strerror(errno));
+ return (0);
+}
+
+#endif /* BSD */
+#endif /* BSD >= 199306 */
diff --git a/libexec/bootpd/syslog.conf b/libexec/bootpd/syslog.conf
new file mode 100644
index 000000000000..2c135af4974e
--- /dev/null
+++ b/libexec/bootpd/syslog.conf
@@ -0,0 +1,63 @@
+#
+# syslog configuration file for SunOS 4.X
+# (modified to do local2 separately)
+#
+# This file is processed by m4 so be careful to quote (`') names
+# that match m4 reserved words. Also, within ifdef's, arguments
+# containing commas must be quoted.
+#
+# Note: Have to exclude user from most lines so that user.alert
+# and user.emerg are not included, because old sendmails
+# will generate them for debugging information. If you
+# have no 4.2BSD based systems doing network logging, you
+# can remove all the special cases for "user" logging.
+
+#*.err;kern.debug;auth.notice;user.none /dev/console
+kern.debug;user,mail.crit;auth.notice /dev/console
+daemon,syslog,lpr,news,uucp,cron.err /dev/console
+
+#*.err;kern.debug;daemon,auth.notice;mail.crit;user.none /var/adm/messages
+kern.debug;user,mail.crit;auth.notice /var/adm/messages
+daemon.notice;syslog,news,uucp,cron.err /var/adm/messages
+
+lpr.debug /var/adm/lpd-errs
+
+*.alert;kern.err;daemon.err;user.none operator
+*.alert;user.none root
+
+*.emerg;user.none *
+
+# for loghost machines, to have authentication messages (su, login, etc.)
+# logged to a file, un-comment out the following line and adjust the file name
+# as appropriate.
+#
+# if a non-loghost machine chooses to have such messages
+# sent to the loghost machine, un-comment out the following line.
+#
+#auth.notice ifdef(`LOGHOST', /var/log/authlog, @loghost)
+
+mail.debug ifdef(`LOGHOST', /var/log/syslog, @loghost)
+
+# following line for compatibility with old sendmails. they will send
+# messages with no facility code, which will be turned into "user" messages
+# by the local syslog daemon. only the "loghost" machine needs the following
+# line, to cause these old sendmail log messages to be logged in the
+# mail syslog file.
+#
+ifdef(`LOGHOST',
+user.alert /var/log/syslog
+)
+#
+# non-loghost machines will use the following lines to cause "user"
+# log messages to be logged locally.
+#
+ifdef(`LOGHOST', ,
+user.err /dev/console
+user.err /var/adm/messages
+user.alert `root, operator'
+user.emerg *
+)
+
+# Local2: (bootpd, pppd)
+local2.debug /dev/console
+#local2.debug /var/log/local2
diff --git a/libexec/bootpd/tools/Makefile b/libexec/bootpd/tools/Makefile
new file mode 100644
index 000000000000..175a2cc2fa60
--- /dev/null
+++ b/libexec/bootpd/tools/Makefile
@@ -0,0 +1,5 @@
+# Makefile
+
+SUBDIR= bootpef bootptest
+
+.include <bsd.subdir.mk>
diff --git a/libexec/bootpd/tools/Makefile.inc b/libexec/bootpd/tools/Makefile.inc
new file mode 100644
index 000000000000..1017c3e74630
--- /dev/null
+++ b/libexec/bootpd/tools/Makefile.inc
@@ -0,0 +1,7 @@
+# Makefile.inc
+
+BINDIR= /usr/sbin
+
+WARNS?= 1
+
+.include "../Makefile.inc"
diff --git a/libexec/bootpd/tools/bootpef/Makefile b/libexec/bootpd/tools/bootpef/Makefile
new file mode 100644
index 000000000000..bf51013c39c7
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/Makefile
@@ -0,0 +1,12 @@
+# Makefile
+
+PROG= bootpef
+MAN= bootpef.8
+SRCS= bootpef.c dovend.c readfile.c hash.c dumptab.c lookup.c \
+ hwaddr.c report.c tzone.c rtmsg.c
+
+SRCDIR= ${.CURDIR}/../..
+CFLAGS+=-I${SRCDIR}
+.PATH: ${SRCDIR}
+
+.include <bsd.prog.mk>
diff --git a/libexec/bootpd/tools/bootpef/Makefile.depend b/libexec/bootpd/tools/bootpef/Makefile.depend
new file mode 100644
index 000000000000..344a5d0e9310
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/bootpd/tools/bootpef/bootpef.8 b/libexec/bootpd/tools/bootpef/bootpef.8
new file mode 100644
index 000000000000..f410dfd9c635
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/bootpef.8
@@ -0,0 +1,63 @@
+.\"
+.\" bootpef.8
+.Dd December 4, 1993
+.Dt BOOTPEF 8
+.Os
+.Sh NAME
+.Nm bootpef
+.Nd "BOOTP Extension File compiler"
+.Sh SYNOPSIS
+.Bk -words
+.Nm
+.Op Fl c Ar chdir\-path
+.Op Fl d Ar debug\-level
+.Op Fl f Ar config\-file
+.Op Ar client\-name ...
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility builds the
+.Em "Extension Path"
+files described by
+.%T "RFC 1497"
+(tag 18).
+If any
+.Ar client\-name
+arguments are specified, then
+.Nm
+compiles the extension files for only those clients.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl c Ar chdir\-path
+Sets the current directory used by
+.Nm
+while creating extension files.
+This is useful when the
+extension file names are specified as relative pathnames, and
+.Nm
+needs to use the same current directory as the TFTP server
+(typically
+.Pa /tftpboot ) .
+.It Fl d Ar debug\-level
+Sets the
+.Ar debug\-level
+variable that controls the amount of debugging messages generated.
+For example,
+.Fl d Ar 4
+will set the debugging level to 4.
+.It Fl f Ar config\-file
+Set the name of the config file that specifies the option
+data to be sent to each client.
+.El
+.Sh SEE ALSO
+.Xr bootpd 8 ,
+.Xr tftpd 8
+.Rs
+.%O RFC951
+.%T "BOOTSTRAP PROTOCOL (BOOTP)"
+.Re
+.Rs
+.%O RFC1497
+.%T "BOOTP Vendor Information Extensions"
+.Re
diff --git a/libexec/bootpd/tools/bootpef/bootpef.c b/libexec/bootpd/tools/bootpef/bootpef.c
new file mode 100644
index 000000000000..f8b2eeeaaf2d
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/bootpef.c
@@ -0,0 +1,318 @@
+/************************************************************************
+ Copyright 1988, 1991 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Carnegie Mellon University not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+/*
+ * bootpef - BOOTP Extension File generator
+ * Makes an "Extension File" for each host entry that
+ * defines an and Extension File. (See RFC1497, tag 18.)
+ *
+ * HISTORY
+ * See ./Changes
+ *
+ * BUGS
+ * See ./ToDo
+ */
+
+
+
+#include <stdarg.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#include "bootp.h"
+#include "hash.h"
+#include "hwaddr.h"
+#include "bootpd.h"
+#include "dovend.h"
+#include "readfile.h"
+#include "report.h"
+#include "tzone.h"
+#include "patchlevel.h"
+
+#define BUFFERSIZE 0x4000
+
+#ifndef CONFIG_FILE
+#define CONFIG_FILE "/etc/bootptab"
+#endif
+
+
+
+/*
+ * Externals, forward declarations, and global variables
+ */
+
+static void mktagfile(struct host *);
+static void usage(void) __dead2;
+
+/*
+ * General
+ */
+
+char *progname;
+char *chdir_path;
+int debug = 0; /* Debugging flag (level) */
+byte *buffer;
+
+/*
+ * Globals below are associated with the bootp database file (bootptab).
+ */
+
+char *bootptab = CONFIG_FILE;
+
+
+/*
+ * Print "usage" message and exit
+ */
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: $s [ -c chdir ] [-d level] [-f configfile] [host...]\n");
+ fprintf(stderr, "\t -c n\tset current directory\n");
+ fprintf(stderr, "\t -d n\tset debug level\n");
+ fprintf(stderr, "\t -f n\tconfig file name\n");
+ exit(1);
+}
+
+
+/*
+ * Initialization such as command-line processing is done and then the
+ * main server loop is started.
+ */
+int
+main(int argc, char **argv)
+{
+ struct host *hp;
+ char *stmp;
+ int n;
+
+ progname = strrchr(argv[0], '/');
+ if (progname) progname++;
+ else progname = argv[0];
+
+ /* Get work space for making tag 18 files. */
+ buffer = (byte *) malloc(BUFFERSIZE);
+ if (!buffer) {
+ report(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+ /*
+ * Set defaults that might be changed by option switches.
+ */
+ stmp = NULL;
+
+ /*
+ * Read switches.
+ */
+ for (argc--, argv++; argc > 0; argc--, argv++) {
+ if (argv[0][0] != '-')
+ break;
+ switch (argv[0][1]) {
+
+ case 'c': /* chdir_path */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (stmp[0] != '/')) {
+ fprintf(stderr,
+ "bootpd: invalid chdir specification\n");
+ break;
+ }
+ chdir_path = stmp;
+ break;
+
+ case 'd': /* debug */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else if (argv[1] && argv[1][0] == '-') {
+ /*
+ * Backwards-compatible behavior:
+ * no parameter, so just increment the debug flag.
+ */
+ debug++;
+ break;
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
+ fprintf(stderr,
+ "bootpd: invalid debug level\n");
+ break;
+ }
+ debug = n;
+ break;
+
+ case 'f': /* config file */
+ if (argv[0][2]) {
+ stmp = &(argv[0][2]);
+ } else {
+ argc--;
+ argv++;
+ stmp = argv[0];
+ }
+ bootptab = stmp;
+ break;
+
+ default:
+ fprintf(stderr, "bootpd: unknown switch: -%c\n",
+ argv[0][1]);
+ usage();
+ break;
+ }
+ }
+
+ /* Get the timezone. */
+ tzone_init();
+
+ /* Allocate hash tables. */
+ rdtab_init();
+
+ /*
+ * Read the bootptab file.
+ */
+ readtab(1); /* force read */
+
+ /* Set the cwd (i.e. to /tftpboot) */
+ if (chdir_path) {
+ if (chdir(chdir_path) < 0)
+ report(LOG_ERR, "%s: chdir failed", chdir_path);
+ }
+ /* If there are host names on the command line, do only those. */
+ if (argc > 0) {
+ unsigned int tlen, hashcode;
+
+ while (argc) {
+ tlen = strlen(argv[0]);
+ hashcode = hash_HashFunction((u_char *)argv[0], tlen);
+ hp = (struct host *) hash_Lookup(nmhashtable,
+ hashcode,
+ nmcmp, argv[0]);
+ if (!hp) {
+ printf("%s: no matching entry\n", argv[0]);
+ exit(1);
+ }
+ if (!hp->flags.exten_file) {
+ printf("%s: no extension file\n", argv[0]);
+ exit(1);
+ }
+ mktagfile(hp);
+ argv++;
+ argc--;
+ }
+ exit(0);
+ }
+ /* No host names specified. Do them all. */
+ hp = (struct host *) hash_FirstEntry(nmhashtable);
+ while (hp != NULL) {
+ mktagfile(hp);
+ hp = (struct host *) hash_NextEntry(nmhashtable);
+ }
+ return (0);
+}
+
+
+
+/*
+ * Make a "TAG 18" file for this host.
+ * (Insert the RFC1497 options.)
+ */
+
+static void
+mktagfile(struct host *hp)
+{
+ FILE *fp;
+ int bytesleft, len;
+ byte *vp;
+
+ if (!hp->flags.exten_file)
+ return;
+
+ vp = buffer;
+ bytesleft = BUFFERSIZE;
+ bcopy(vm_rfc1048, vp, 4); /* Copy in the magic cookie */
+ vp += 4;
+ bytesleft -= 4;
+
+ /*
+ * The "extension file" options are appended by the following
+ * function (which is shared with bootpd.c).
+ */
+ len = dovend_rfc1497(hp, vp, bytesleft);
+ vp += len;
+ bytesleft -= len;
+
+ if (bytesleft < 1) {
+ report(LOG_ERR, "%s: too much option data",
+ hp->exten_file->string);
+ return;
+ }
+ *vp++ = TAG_END;
+ bytesleft--;
+
+ /* Write the buffer to the extension file. */
+ printf("Updating \"%s\"\n", hp->exten_file->string);
+ if ((fp = fopen(hp->exten_file->string, "w")) == NULL) {
+ report(LOG_ERR, "error opening \"%s\": %s",
+ hp->exten_file->string, get_errmsg());
+ return;
+ }
+ len = vp - buffer;
+ if (len != fwrite(buffer, 1, len, fp)) {
+ report(LOG_ERR, "write failed on \"%s\" : %s",
+ hp->exten_file->string, get_errmsg());
+ }
+ fclose(fp);
+
+} /* mktagfile */
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/tools/bootptest/Makefile b/libexec/bootpd/tools/bootptest/Makefile
new file mode 100644
index 000000000000..3a6fbd584606
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/Makefile
@@ -0,0 +1,11 @@
+# Makefile
+
+PROG= bootptest
+MAN= bootptest.8
+SRCS= bootptest.c getether.c getif.c print-bootp.c report.c
+
+SRCDIR= ${.CURDIR}/../..
+CFLAGS+=-I${SRCDIR}
+.PATH: ${SRCDIR}
+
+.include <bsd.prog.mk>
diff --git a/libexec/bootpd/tools/bootptest/Makefile.depend b/libexec/bootpd/tools/bootptest/Makefile.depend
new file mode 100644
index 000000000000..344a5d0e9310
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/bootpd/tools/bootptest/bootptest.8 b/libexec/bootpd/tools/bootptest/bootptest.8
new file mode 100644
index 000000000000..97de293d6590
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/bootptest.8
@@ -0,0 +1,76 @@
+.\"
+.\" bootptest.8
+.Dd June 10, 1993
+.Dt BOOTPTEST 8
+.Os
+.Sh NAME
+.Nm bootptest
+.Nd "send BOOTP queries and print responses"
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar bootfile
+.Op Fl h
+.Op Fl m Ar magic_number
+.Ar server\-name
+.Op Ar template\-file
+.Sh DESCRIPTION
+The
+.Nm
+utility sends BOOTP requests to the host specified as
+.Ar server\-name
+at one\-second intervals until either a response is received,
+or until ten requests have gone unanswered.
+After a response is received,
+.Nm
+will wait one more second listening for additional responses.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl f Ar bootfile
+Fill in the boot file field of the request with
+.Ar bootfile .
+.It Fl h
+Use the hardware (Ethernet) address to identify the client.
+By default, the IP address is copied into the request
+indicating that this client already knows its IP address.
+.It Fl m Ar magic_number
+Initialize the first word of the vendor options field with
+.Ar magic_number .
+.El
+.Pp
+A
+.Ar template\-file
+may be specified, in which case
+.Nm
+uses the (binary) contents of this file to initialize the
+.Em options
+area of the request packet.
+.Sh SEE ALSO
+.Xr bootpd 8
+.Rs
+.%O RFC951
+.%T "BOOTSTRAP PROTOCOL (BOOTP)"
+.Re
+.Rs
+.%O RFC1048
+.%T "BOOTP Vendor Information Extensions"
+.Re
+.Sh AUTHORS
+The
+.Nm
+utility is a combination of original and derived works.
+The main program module
+.Pq Pa bootptest.c
+is original work by
+.An Gordon W. Ross Aq Mt gwr@mc.com .
+The packet printing module
+.Pq Pa print\-bootp.c
+is a slightly modified
+version of a file from the
+.Bx
+.Xr tcpdump 1
+program.
+.Pp
+This program includes software developed by the University of
+California, Lawrence Berkeley Laboratory and its contributors.
+(See the copyright notice in
+.Pa print\-bootp.c . )
diff --git a/libexec/bootpd/tools/bootptest/bootptest.c b/libexec/bootpd/tools/bootptest/bootptest.c
new file mode 100644
index 000000000000..1797bcb9574d
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/bootptest.c
@@ -0,0 +1,510 @@
+/*
+ * bootptest.c - Test out a bootp server.
+ *
+ * This simple program was put together from pieces taken from
+ * various places, including the CMU BOOTP client and server.
+ * The packet printing routine is from the Berkeley "tcpdump"
+ * program with some enhancements I added. The print-bootp.c
+ * file was shared with my copy of "tcpdump" and therefore uses
+ * some unusual utility routines that would normally be provided
+ * by various parts of the tcpdump program. Gordon W. Ross
+ *
+ * Boilerplate:
+ *
+ * This program includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory and its contributors.
+ * (See the copyright notice in print-bootp.c)
+ *
+ * The remainder of this program is public domain. You may do
+ * whatever you like with it except claim that you wrote it.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * HISTORY:
+ *
+ * 12/02/93 Released version 1.4 (with bootp-2.3.2)
+ * 11/05/93 Released version 1.3
+ * 10/14/93 Released version 1.2
+ * 10/11/93 Released version 1.1
+ * 09/28/93 Released version 1.0
+ * 09/93 Original developed by Gordon W. Ross <gwr@mc.com>
+ *
+ */
+
+#include <sys/cdefs.h>
+char *usage = "bootptest [-h] server-name [vendor-data-template-file]";
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+
+#include <err.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <assert.h>
+
+#include "bootp.h"
+#include "bootptest.h"
+#include "getif.h"
+#include "getether.h"
+
+#include "patchlevel.h"
+
+static void send_request(int s);
+
+#define LOG_ERR 1
+#define BUFLEN 1024
+#define WAITSECS 1
+#define MAXWAIT 10
+
+int vflag = 1;
+int tflag = 0;
+int thiszone;
+char *progname;
+unsigned char *packetp;
+unsigned char *snapend;
+int snaplen;
+
+
+/*
+ * IP port numbers for client and server obtained from /etc/services
+ */
+
+u_short bootps_port, bootpc_port;
+
+
+/*
+ * Internet socket and interface config structures
+ */
+
+struct sockaddr_in sin_server; /* where to send requests */
+struct sockaddr_in sin_client; /* for bind and listen */
+struct sockaddr_in sin_from; /* Packet source */
+u_char eaddr[16]; /* Ethernet address */
+
+/*
+ * General
+ */
+
+int debug = 1; /* Debugging flag (level) */
+char *sndbuf; /* Send packet buffer */
+char *rcvbuf; /* Receive packet buffer */
+
+struct utsname my_uname;
+char *hostname;
+
+/*
+ * Vendor magic cookies for CMU and RFC1048
+ */
+
+unsigned char vm_cmu[4] = VM_CMU;
+unsigned char vm_rfc1048[4] = VM_RFC1048;
+short secs; /* How long client has waited */
+
+/*
+ * Initialization such as command-line processing is done, then
+ * the receiver loop is started. Die when interrupted.
+ */
+
+int
+main(int argc, char **argv)
+{
+ struct bootp *bp;
+ struct servent *sep;
+ struct hostent *hep;
+
+ char *servername = NULL;
+ char *vendor_file = NULL;
+ char *bp_file = NULL;
+ int32 server_addr; /* inet addr, network order */
+ int s; /* Socket file descriptor */
+ int n, fromlen, recvcnt;
+ int use_hwa = 0;
+ int32 vend_magic;
+ int32 xid;
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+ argc--;
+ argv++;
+
+ if (debug)
+ printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL);
+
+ /*
+ * Verify that "struct bootp" has the correct official size.
+ * (Catch evil compilers that do struct padding.)
+ */
+ assert(sizeof(struct bootp) == BP_MINPKTSZ);
+
+ if (uname(&my_uname) < 0)
+ errx(1, "can't get hostname");
+ hostname = my_uname.nodename;
+
+ sndbuf = malloc(BUFLEN);
+ rcvbuf = malloc(BUFLEN);
+ if (!sndbuf || !rcvbuf) {
+ printf("malloc failed\n");
+ exit(1);
+ }
+
+ /* default magic number */
+ bcopy(vm_rfc1048, (char*)&vend_magic, 4);
+
+ /* Handle option switches. */
+ while (argc > 0) {
+ if (argv[0][0] != '-')
+ break;
+ switch (argv[0][1]) {
+
+ case 'f': /* File name to request. */
+ if (argc < 2)
+ goto error;
+ argc--; argv++;
+ bp_file = *argv;
+ break;
+
+ case 'h': /* Use hardware address. */
+ use_hwa = 1;
+ break;
+
+ case 'm': /* Magic number value. */
+ if (argc < 2)
+ goto error;
+ argc--; argv++;
+ vend_magic = inet_addr(*argv);
+ break;
+
+ error:
+ default:
+ puts(usage);
+ exit(1);
+
+ }
+ argc--;
+ argv++;
+ }
+
+ /* Get server name (or address) for query. */
+ if (argc > 0) {
+ servername = *argv;
+ argc--;
+ argv++;
+ }
+ /* Get optional vendor-data-template-file. */
+ if (argc > 0) {
+ vendor_file = *argv;
+ argc--;
+ argv++;
+ }
+ if (!servername) {
+ printf("missing server name.\n");
+ puts(usage);
+ exit(1);
+ }
+ /*
+ * Create a socket.
+ */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+ /*
+ * Get server's listening port number
+ */
+ sep = getservbyname("bootps", "udp");
+ if (sep) {
+ bootps_port = ntohs((u_short) sep->s_port);
+ } else {
+ warnx("bootps/udp: unknown service -- using port %d",
+ IPPORT_BOOTPS);
+ bootps_port = (u_short) IPPORT_BOOTPS;
+ }
+
+ /*
+ * Set up server socket address (for send)
+ */
+ if (servername) {
+ if (isdigit(servername[0]))
+ server_addr = inet_addr(servername);
+ else {
+ hep = gethostbyname(servername);
+ if (!hep)
+ errx(1, "%s: unknown host", servername);
+ bcopy(hep->h_addr, &server_addr, sizeof(server_addr));
+ }
+ } else {
+ /* Get broadcast address */
+ /* XXX - not yet */
+ server_addr = INADDR_ANY;
+ }
+ sin_server.sin_family = AF_INET;
+ sin_server.sin_port = htons(bootps_port);
+ sin_server.sin_addr.s_addr = server_addr;
+
+ /*
+ * Get client's listening port number
+ */
+ sep = getservbyname("bootpc", "udp");
+ if (sep) {
+ bootpc_port = ntohs(sep->s_port);
+ } else {
+ warnx("bootpc/udp: unknown service -- using port %d",
+ IPPORT_BOOTPC);
+ bootpc_port = (u_short) IPPORT_BOOTPC;
+ }
+
+ /*
+ * Set up client socket address (for listen)
+ */
+ sin_client.sin_family = AF_INET;
+ sin_client.sin_port = htons(bootpc_port);
+ sin_client.sin_addr.s_addr = INADDR_ANY;
+
+ /*
+ * Bind client socket to BOOTPC port.
+ */
+ if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) {
+ if (errno == EACCES) {
+ warn("bind BOOTPC port");
+ errx(1, "you need to run this as root");
+ }
+ else
+ err(1, "bind BOOTPC port");
+ }
+ /*
+ * Build a request.
+ */
+ bp = (struct bootp *) sndbuf;
+ bzero(bp, sizeof(*bp));
+ bp->bp_op = BOOTREQUEST;
+ xid = (int32) getpid();
+ bp->bp_xid = (u_int32) htonl(xid);
+ if (bp_file)
+ strncpy(bp->bp_file, bp_file, BP_FILE_LEN);
+
+ /*
+ * Fill in the hardware address (or client IP address)
+ */
+ if (use_hwa) {
+ struct ifreq *ifr;
+
+ ifr = getif(s, &sin_server.sin_addr);
+ if (!ifr) {
+ printf("No interface for %s\n", servername);
+ exit(1);
+ }
+ if (getether(ifr->ifr_name, (char*)eaddr)) {
+ printf("Can not get ether addr for %s\n", ifr->ifr_name);
+ exit(1);
+ }
+ /* Copy Ethernet address into request packet. */
+ bp->bp_htype = 1;
+ bp->bp_hlen = 6;
+ bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen);
+ } else {
+ /* Fill in the client IP address. */
+ hep = gethostbyname(hostname);
+ if (!hep) {
+ printf("Can not get my IP address\n");
+ exit(1);
+ }
+ bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length);
+ }
+
+ /*
+ * Copy in the default vendor data.
+ */
+ bcopy((char*)&vend_magic, bp->bp_vend, 4);
+ if (vend_magic)
+ bp->bp_vend[4] = TAG_END;
+
+ /*
+ * Read in the "options" part of the request.
+ * This also determines the size of the packet.
+ */
+ snaplen = sizeof(*bp);
+ if (vendor_file) {
+ int fd = open(vendor_file, 0);
+ if (fd < 0) {
+ perror(vendor_file);
+ exit(1);
+ }
+ /* Compute actual space for options. */
+ n = BUFLEN - sizeof(*bp) + BP_VEND_LEN;
+ n = read(fd, bp->bp_vend, n);
+ close(fd);
+ if (n < 0) {
+ perror(vendor_file);
+ exit(1);
+ }
+ printf("read %d bytes of vendor template\n", n);
+ if (n > BP_VEND_LEN) {
+ printf("warning: extended options in use (len > %d)\n",
+ BP_VEND_LEN);
+ snaplen += (n - BP_VEND_LEN);
+ }
+ }
+ /*
+ * Set globals needed by print_bootp
+ * (called by send_request)
+ */
+ packetp = (unsigned char *) eaddr;
+ snapend = (unsigned char *) sndbuf + snaplen;
+
+ /* Send a request once per second while waiting for replies. */
+ recvcnt = 0;
+ bp->bp_secs = secs = 0;
+ send_request(s);
+ while (1) {
+ struct timeval tv;
+ int readfds;
+
+ tv.tv_sec = WAITSECS;
+ tv.tv_usec = 0L;
+ readfds = (1 << s);
+ n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv);
+ if (n < 0) {
+ perror("select");
+ break;
+ }
+ if (n == 0) {
+ /*
+ * We have not received a response in the last second.
+ * If we have ever received any responses, exit now.
+ * Otherwise, bump the "wait time" field and re-send.
+ */
+ if (recvcnt > 0)
+ exit(0);
+ secs += WAITSECS;
+ if (secs > MAXWAIT)
+ break;
+ bp->bp_secs = htons(secs);
+ send_request(s);
+ continue;
+ }
+ fromlen = sizeof(sin_from);
+ n = recvfrom(s, rcvbuf, BUFLEN, 0,
+ (struct sockaddr *) &sin_from, &fromlen);
+ if (n <= 0) {
+ continue;
+ }
+ if (n < sizeof(struct bootp)) {
+ printf("received short packet\n");
+ continue;
+ }
+ recvcnt++;
+
+ /* Print the received packet. */
+ printf("Recvd from %s", inet_ntoa(sin_from.sin_addr));
+ /* set globals needed by bootp_print() */
+ snaplen = n;
+ snapend = (unsigned char *) rcvbuf + snaplen;
+ bootp_print((struct bootp *)rcvbuf, n, sin_from.sin_port, 0);
+ putchar('\n');
+ /*
+ * This no longer exits immediately after receiving
+ * one response because it is useful to know if the
+ * client might get multiple responses. This code
+ * will now listen for one second after a response.
+ */
+ }
+ errx(1, "no response from %s", servername);
+}
+
+static void
+send_request(int s)
+{
+ /* Print the request packet. */
+ printf("Sending to %s", inet_ntoa(sin_server.sin_addr));
+ bootp_print((struct bootp *)sndbuf, snaplen, sin_from.sin_port, 0);
+ putchar('\n');
+
+ /* Send the request packet. */
+ if (sendto(s, sndbuf, snaplen, 0,
+ (struct sockaddr *) &sin_server,
+ sizeof(sin_server)) < 0)
+ {
+ perror("sendto server");
+ exit(1);
+ }
+}
+
+/*
+ * Print out a filename (or other ascii string).
+ * Return true if truncated.
+ */
+int
+printfn(u_char *s, u_char *ep)
+{
+ u_char c;
+
+ putchar('"');
+ while ((c = *s++) != '\0') {
+ if (s > ep) {
+ putchar('"');
+ return (1);
+ }
+ if (!isascii(c)) {
+ c = toascii(c);
+ putchar('M');
+ putchar('-');
+ }
+ if (!isprint(c)) {
+ c ^= 0x40; /* DEL to ?, others to alpha */
+ putchar('^');
+ }
+ putchar(c);
+ }
+ putchar('"');
+ return (0);
+}
+
+/*
+ * Convert an IP addr to a string.
+ * (like inet_ntoa, but ina is a pointer)
+ */
+char *
+ipaddr_string(struct in_addr *ina)
+{
+ static char b[24];
+ u_char *p;
+
+ p = (u_char *) ina;
+ snprintf(b, sizeof(b), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ return (b);
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/tools/bootptest/bootptest.h b/libexec/bootpd/tools/bootptest/bootptest.h
new file mode 100644
index 000000000000..e4da8c6bbb47
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/bootptest.h
@@ -0,0 +1,17 @@
+/* bootptest.h */
+/*
+ * Hacks for sharing print-bootp.c between tcpdump and bootptest.
+ */
+#define ESRC(p) (p)
+#define EDST(p) (p)
+
+extern int vflag; /* verbose flag */
+
+/* global pointers to beginning and end of current packet (during printing) */
+extern unsigned char *packetp;
+extern unsigned char *snapend;
+
+void bootp_print(struct bootp *bp, int length, u_short sport,
+ u_short dport);
+char *ipaddr_string(struct in_addr *);
+int printfn(u_char *s, u_char *ep);
diff --git a/libexec/bootpd/tools/bootptest/print-bootp.c b/libexec/bootpd/tools/bootptest/print-bootp.c
new file mode 100644
index 000000000000..dabab8115313
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/print-bootp.c
@@ -0,0 +1,476 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988-1990
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that:
+ * 1. Source code distributions retain the above copyright
+ * notice and this paragraph in its entirety
+ * 2. Distributions including binary code include the above copyright
+ * notice and this paragraph in its entirety in the documentation
+ * or other materials provided with the distribution, and
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Format and print bootp packets.
+ *
+ * This file was copied from tcpdump-2.1.1 and modified.
+ * There is an e-mail list for tcpdump: <tcpdump@ee.lbl.gov>
+ */
+
+#include <stdio.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <sys/time.h> /* for struct timeval in net/if.h */
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <string.h>
+#include <ctype.h>
+
+#include "bootp.h"
+#include "bootptest.h"
+
+/* These decode the vendor data. */
+static void rfc1048_print(u_char *bp, int length);
+static void cmu_print(u_char *bp, int length);
+static void other_print(u_char *bp, int length);
+static void dump_hex(u_char *bp, int len);
+
+/*
+ * Print bootp requests
+ */
+void
+bootp_print(struct bootp *bp, int length, u_short sport, u_short dport)
+{
+ static char tstr[] = " [|bootp]";
+ static unsigned char vm_cmu[4] = VM_CMU;
+ static unsigned char vm_rfc1048[4] = VM_RFC1048;
+ u_char *ep;
+ int vdlen;
+
+#define TCHECK(var, l) if ((u_char *)&(var) > ep - l) goto trunc
+
+ /* Note funny sized packets */
+ if (length != sizeof(struct bootp))
+ (void) printf(" [len=%d]", length);
+
+ /* 'ep' points to the end of available data. */
+ ep = (u_char *) snapend;
+
+ switch (bp->bp_op) {
+
+ case BOOTREQUEST:
+ /* Usually, a request goes from a client to a server */
+ if (sport != IPPORT_BOOTPC || dport != IPPORT_BOOTPS)
+ printf(" (request)");
+ break;
+
+ case BOOTREPLY:
+ /* Usually, a reply goes from a server to a client */
+ if (sport != IPPORT_BOOTPS || dport != IPPORT_BOOTPC)
+ printf(" (reply)");
+ break;
+
+ default:
+ printf(" bootp-#%d", bp->bp_op);
+ }
+
+ /* The usual hardware address type is 1 (10Mb Ethernet) */
+ if (bp->bp_htype != 1)
+ printf(" htype:%d", bp->bp_htype);
+
+ /* The usual length for 10Mb Ethernet address is 6 bytes */
+ if (bp->bp_hlen != 6)
+ printf(" hlen:%d", bp->bp_hlen);
+
+ /* Client's Hardware address */
+ if (bp->bp_hlen) {
+ struct ether_header *eh;
+ char *e;
+
+ TCHECK(bp->bp_chaddr[0], 6);
+ eh = (struct ether_header *) packetp;
+ if (bp->bp_op == BOOTREQUEST)
+ e = (char *) ESRC(eh);
+ else if (bp->bp_op == BOOTREPLY)
+ e = (char *) EDST(eh);
+ else
+ e = NULL;
+ if (e == NULL || bcmp((char *) bp->bp_chaddr, e, 6))
+ dump_hex(bp->bp_chaddr, bp->bp_hlen);
+ }
+ /* Only print interesting fields */
+ if (bp->bp_hops)
+ printf(" hops:%d", bp->bp_hops);
+
+ if (bp->bp_xid)
+ printf(" xid:%ld", (long)ntohl(bp->bp_xid));
+
+ if (bp->bp_secs)
+ printf(" secs:%d", ntohs(bp->bp_secs));
+
+ /* Client's ip address */
+ TCHECK(bp->bp_ciaddr, sizeof(bp->bp_ciaddr));
+ if (bp->bp_ciaddr.s_addr)
+ printf(" C:%s", ipaddr_string(&bp->bp_ciaddr));
+
+ /* 'your' ip address (bootp client) */
+ TCHECK(bp->bp_yiaddr, sizeof(bp->bp_yiaddr));
+ if (bp->bp_yiaddr.s_addr)
+ printf(" Y:%s", ipaddr_string(&bp->bp_yiaddr));
+
+ /* Server's ip address */
+ TCHECK(bp->bp_siaddr, sizeof(bp->bp_siaddr));
+ if (bp->bp_siaddr.s_addr)
+ printf(" S:%s", ipaddr_string(&bp->bp_siaddr));
+
+ /* Gateway's ip address */
+ TCHECK(bp->bp_giaddr, sizeof(bp->bp_giaddr));
+ if (bp->bp_giaddr.s_addr)
+ printf(" G:%s", ipaddr_string(&bp->bp_giaddr));
+
+ TCHECK(bp->bp_sname[0], sizeof(bp->bp_sname));
+ if (*bp->bp_sname) {
+ printf(" sname:");
+ if (printfn(bp->bp_sname, ep)) {
+ fputs(tstr + 1, stdout);
+ return;
+ }
+ }
+ TCHECK(bp->bp_file[0], sizeof(bp->bp_file));
+ if (*bp->bp_file) {
+ printf(" file:");
+ if (printfn(bp->bp_file, ep)) {
+ fputs(tstr + 1, stdout);
+ return;
+ }
+ }
+ /* Don't try to decode the vendor buffer unless we're verbose */
+ if (vflag <= 0)
+ return;
+
+ vdlen = sizeof(bp->bp_vend);
+ /* Vendor data can extend to the end of the packet. */
+ if (vdlen < (ep - bp->bp_vend))
+ vdlen = (ep - bp->bp_vend);
+
+ TCHECK(bp->bp_vend[0], vdlen);
+ printf(" vend");
+ if (!bcmp(bp->bp_vend, vm_rfc1048, sizeof(u_int32)))
+ rfc1048_print(bp->bp_vend, vdlen);
+ else if (!bcmp(bp->bp_vend, vm_cmu, sizeof(u_int32)))
+ cmu_print(bp->bp_vend, vdlen);
+ else
+ other_print(bp->bp_vend, vdlen);
+
+ return;
+ trunc:
+ fputs(tstr, stdout);
+#undef TCHECK
+}
+
+/*
+ * Option description data follows.
+ * These are described in: RFC-1048, RFC-1395, RFC-1497, RFC-1533
+ *
+ * The first char of each option string encodes the data format:
+ * ?: unknown
+ * a: ASCII
+ * b: byte (8-bit)
+ * i: inet address
+ * l: int32
+ * s: short (16-bit)
+ */
+char *
+rfc1048_opts[] = {
+ /* Originally from RFC-1048: */
+ "?PAD", /* 0: Padding - special, no data. */
+ "iSM", /* 1: subnet mask (RFC950)*/
+ "lTZ", /* 2: time offset, seconds from UTC */
+ "iGW", /* 3: gateways (or routers) */
+ "iTS", /* 4: time servers (RFC868) */
+ "iINS", /* 5: IEN name servers (IEN116) */
+ "iDNS", /* 6: domain name servers (RFC1035)(1034?) */
+ "iLOG", /* 7: MIT log servers */
+ "iCS", /* 8: cookie servers (RFC865) */
+ "iLPR", /* 9: lpr server (RFC1179) */
+ "iIPS", /* 10: impress servers (Imagen) */
+ "iRLP", /* 11: resource location servers (RFC887) */
+ "aHN", /* 12: host name (ASCII) */
+ "sBFS", /* 13: boot file size (in 512 byte blocks) */
+
+ /* Added by RFC-1395: */
+ "aDUMP", /* 14: Merit Dump File */
+ "aDNAM", /* 15: Domain Name (for DNS) */
+ "iSWAP", /* 16: Swap Server */
+ "aROOT", /* 17: Root Path */
+
+ /* Added by RFC-1497: */
+ "aEXTF", /* 18: Extensions Path (more options) */
+
+ /* Added by RFC-1533: (many, many options...) */
+#if 1 /* These might not be worth recognizing by name. */
+
+ /* IP Layer Parameters, per-host (RFC-1533, sect. 4) */
+ "bIP-forward", /* 19: IP Forwarding flag */
+ "bIP-srcroute", /* 20: IP Source Routing Enable flag */
+ "iIP-filters", /* 21: IP Policy Filter (addr pairs) */
+ "sIP-maxudp", /* 22: IP Max-UDP reassembly size */
+ "bIP-ttlive", /* 23: IP Time to Live */
+ "lIP-pmtuage", /* 24: IP Path MTU aging timeout */
+ "sIP-pmtutab", /* 25: IP Path MTU plateau table */
+
+ /* IP parameters, per-interface (RFC-1533, sect. 5) */
+ "sIP-mtu-sz", /* 26: IP MTU size */
+ "bIP-mtu-sl", /* 27: IP MTU all subnets local */
+ "bIP-bcast1", /* 28: IP Broadcast Addr ones flag */
+ "bIP-mask-d", /* 29: IP do mask discovery */
+ "bIP-mask-s", /* 30: IP do mask supplier */
+ "bIP-rt-dsc", /* 31: IP do router discovery */
+ "iIP-rt-sa", /* 32: IP router solicitation addr */
+ "iIP-routes", /* 33: IP static routes (dst,router) */
+
+ /* Link Layer parameters, per-interface (RFC-1533, sect. 6) */
+ "bLL-trailer", /* 34: do trailer encapsulation */
+ "lLL-arp-tmo", /* 35: ARP cache timeout */
+ "bLL-ether2", /* 36: Ethernet version 2 (IEEE 802.3) */
+
+ /* TCP parameters (RFC-1533, sect. 7) */
+ "bTCP-def-ttl", /* 37: default time to live */
+ "lTCP-KA-tmo", /* 38: keepalive time interval */
+ "bTCP-KA-junk", /* 39: keepalive sends extra junk */
+
+ /* Application and Service Parameters (RFC-1533, sect. 8) */
+ "aNISDOM", /* 40: NIS Domain (Sun YP) */
+ "iNISSRV", /* 41: NIS Servers */
+ "iNTPSRV", /* 42: NTP (time) Servers (RFC 1129) */
+ "?VSINFO", /* 43: Vendor Specific Info (encapsulated) */
+ "iNBiosNS", /* 44: NetBIOS Name Server (RFC-1001,1..2) */
+ "iNBiosDD", /* 45: NetBIOS Datagram Dist. Server. */
+ "bNBiosNT", /* 46: NetBIOS Note Type */
+ "?NBiosS", /* 47: NetBIOS Scope */
+ "iXW-FS", /* 48: X Window System Font Servers */
+ "iXW-DM", /* 49: X Window System Display Managers */
+
+ /* DHCP extensions (RFC-1533, sect. 9) */
+#endif
+};
+#define KNOWN_OPTIONS (sizeof(rfc1048_opts) / sizeof(rfc1048_opts[0]))
+
+static void
+rfc1048_print(u_char *bp, int length)
+{
+ u_char tag;
+ u_char *ep;
+ int len;
+ u_int32 ul;
+ u_short us;
+ struct in_addr ia;
+ char *optstr;
+
+ printf("-rfc1395");
+
+ /* Step over magic cookie */
+ bp += sizeof(int32);
+ /* Setup end pointer */
+ ep = bp + length;
+ while (bp < ep) {
+ tag = *bp++;
+ /* Check for tags with no data first. */
+ if (tag == TAG_PAD)
+ continue;
+ if (tag == TAG_END)
+ return;
+ if (tag < KNOWN_OPTIONS) {
+ optstr = rfc1048_opts[tag];
+ printf(" %s:", optstr + 1);
+ } else {
+ printf(" T%d:", tag);
+ optstr = "?";
+ }
+ /* Now scan the length byte. */
+ len = *bp++;
+ if (bp + len > ep) {
+ /* truncated option */
+ printf(" |(%d>%td)", len, ep - bp);
+ return;
+ }
+ /* Print the option value(s). */
+ switch (optstr[0]) {
+
+ case 'a': /* ASCII string */
+ printfn(bp, bp + len);
+ bp += len;
+ len = 0;
+ break;
+
+ case 's': /* Word formats */
+ while (len >= 2) {
+ bcopy((char *) bp, (char *) &us, 2);
+ printf("%d", ntohs(us));
+ bp += 2;
+ len -= 2;
+ if (len) printf(",");
+ }
+ if (len) printf("(junk=%d)", len);
+ break;
+
+ case 'l': /* Long words */
+ while (len >= 4) {
+ bcopy((char *) bp, (char *) &ul, 4);
+ printf("%ld", (long)ntohl(ul));
+ bp += 4;
+ len -= 4;
+ if (len) printf(",");
+ }
+ if (len) printf("(junk=%d)", len);
+ break;
+
+ case 'i': /* INET addresses */
+ while (len >= 4) {
+ bcopy((char *) bp, (char *) &ia, 4);
+ printf("%s", ipaddr_string(&ia));
+ bp += 4;
+ len -= 4;
+ if (len) printf(",");
+ }
+ if (len) printf("(junk=%d)", len);
+ break;
+
+ case 'b':
+ default:
+ break;
+
+ } /* switch */
+
+ /* Print as characters, if appropriate. */
+ if (len) {
+ dump_hex(bp, len);
+ if (isascii(*bp) && isprint(*bp)) {
+ printf("(");
+ printfn(bp, bp + len);
+ printf(")");
+ }
+ bp += len;
+ len = 0;
+ }
+ } /* while bp < ep */
+}
+
+static void
+cmu_print(u_char *bp, int length)
+{
+ struct cmu_vend *v;
+
+ printf("-cmu");
+
+ v = (struct cmu_vend *) bp;
+ if (length < sizeof(*v)) {
+ printf(" |L=%d", length);
+ return;
+ }
+
+ /* Subnet mask */
+ if (v->v_flags & VF_SMASK) {
+ printf(" SM:%s", ipaddr_string(&v->v_smask));
+ }
+ /* Default gateway */
+ if (v->v_dgate.s_addr)
+ printf(" GW:%s", ipaddr_string(&v->v_dgate));
+
+ /* Domain name servers */
+ if (v->v_dns1.s_addr)
+ printf(" DNS1:%s", ipaddr_string(&v->v_dns1));
+ if (v->v_dns2.s_addr)
+ printf(" DNS2:%s", ipaddr_string(&v->v_dns2));
+
+ /* IEN-116 name servers */
+ if (v->v_ins1.s_addr)
+ printf(" INS1:%s", ipaddr_string(&v->v_ins1));
+ if (v->v_ins2.s_addr)
+ printf(" INS2:%s", ipaddr_string(&v->v_ins2));
+
+ /* Time servers */
+ if (v->v_ts1.s_addr)
+ printf(" TS1:%s", ipaddr_string(&v->v_ts1));
+ if (v->v_ts2.s_addr)
+ printf(" TS2:%s", ipaddr_string(&v->v_ts2));
+
+}
+
+
+/*
+ * Print out arbitrary, unknown vendor data.
+ */
+
+static void
+other_print(u_char *bp, int length)
+{
+ u_char *ep; /* end pointer */
+ u_char *zp; /* points one past last non-zero byte */
+
+ /* Setup end pointer */
+ ep = bp + length;
+
+ /* Find the last non-zero byte. */
+ for (zp = ep; zp > bp; zp--) {
+ if (zp[-1] != 0)
+ break;
+ }
+
+ /* Print the all-zero case in a compact representation. */
+ if (zp == bp) {
+ printf("-all-zero");
+ return;
+ }
+ printf("-unknown");
+
+ /* Are there enough trailing zeros to make "00..." worthwhile? */
+ if (zp + 2 > ep)
+ zp = ep; /* print them all normally */
+
+ /* Now just print all the non-zero data. */
+ while (bp < zp) {
+ printf(".%02X", *bp);
+ bp++;
+ }
+
+ if (zp < ep)
+ printf(".00...");
+
+ return;
+}
+
+static void
+dump_hex(u_char *bp, int len)
+{
+ while (len > 0) {
+ printf("%02X", *bp);
+ bp++;
+ len--;
+ if (len) printf(".");
+ }
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/trygetea.c b/libexec/bootpd/trygetea.c
new file mode 100644
index 000000000000..5510995386e9
--- /dev/null
+++ b/libexec/bootpd/trygetea.c
@@ -0,0 +1,53 @@
+/*
+ * trygetea.c - test program for getether.c
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+
+#ifdef _AIX32
+#include <sys/time.h> /* for struct timeval in net/if.h */
+#endif
+#include <net/if.h> /* for struct ifreq */
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#include <netdb.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "getether.h"
+
+int debug = 0;
+char *progname;
+
+void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ u_char ea[16]; /* Ethernet address */
+ int i;
+
+ progname = argv[0]; /* for report */
+
+ if (argc < 2) {
+ printf("need interface name\n");
+ exit(1);
+ }
+ if ((i = getether(argv[1], (char*)ea)) < 0) {
+ printf("Could not get Ethernet address (rc=%d)\n", i);
+ exit(1);
+ }
+ printf("Ether-addr");
+ for (i = 0; i < 6; i++)
+ printf(":%x", ea[i] & 0xFF);
+ printf("\n");
+
+ exit(0);
+}
diff --git a/libexec/bootpd/trygetif.c b/libexec/bootpd/trygetif.c
new file mode 100644
index 000000000000..8c78ee34247a
--- /dev/null
+++ b/libexec/bootpd/trygetif.c
@@ -0,0 +1,72 @@
+/*
+ * trygetif.c - test program for getif.c
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+
+#ifdef _AIX32
+#include <sys/time.h> /* for struct timeval in net/if.h */
+#endif
+#include <net/if.h> /* for struct ifreq */
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#include <netdb.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "getif.h"
+
+int debug = 0;
+char *progname;
+
+void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct hostent *hep;
+ struct sockaddr_in *sip; /* Interface address */
+ struct ifreq *ifr;
+ struct in_addr dst_addr;
+ struct in_addr *dap;
+ int s;
+
+ progname = argv[0]; /* for report */
+
+ dap = NULL;
+ if (argc > 1) {
+ dap = &dst_addr;
+ if (isdigit(argv[1][0]))
+ dst_addr.s_addr = inet_addr(argv[1]);
+ else {
+ hep = gethostbyname(argv[1]);
+ if (!hep) {
+ printf("gethostbyname(%s)\n", argv[1]);
+ exit(1);
+ }
+ memcpy(&dst_addr, hep->h_addr, sizeof(dst_addr));
+ }
+ }
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket open");
+ exit(1);
+ }
+ ifr = getif(s, dap);
+ if (!ifr) {
+ printf("no interface for address\n");
+ exit(1);
+ }
+ printf("Intf-name:%s\n", ifr->ifr_name);
+ sip = (struct sockaddr_in *) &(ifr->ifr_addr);
+ printf("Intf-addr:%s\n", inet_ntoa(sip->sin_addr));
+
+ exit(0);
+}
diff --git a/libexec/bootpd/trylook.c b/libexec/bootpd/trylook.c
new file mode 100644
index 000000000000..0479166d2f35
--- /dev/null
+++ b/libexec/bootpd/trylook.c
@@ -0,0 +1,56 @@
+/*
+ * trylook.c - test program for lookup.c
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#include "report.h"
+#include "lookup.h"
+
+extern char *ether_ntoa();
+extern char *inet_ntoa();
+
+int debug = 0;
+char *progname;
+
+void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ struct in_addr in;
+ char *a;
+ u_char *hwa;
+
+ progname = argv[0]; /* for report */
+
+ for (i = 1; i < argc; i++) {
+
+ /* Host name */
+ printf("%s:", argv[i]);
+
+ /* IP addr */
+ if (lookup_ipa(argv[i], &in.s_addr))
+ a = "?";
+ else
+ a = inet_ntoa(in);
+ printf(" ipa=%s", a);
+
+ /* Ether addr */
+ printf(" hwa=");
+ hwa = lookup_hwa(argv[i], 1);
+ if (!hwa)
+ printf("?\n");
+ else {
+ int i;
+ for (i = 0; i < 6; i++)
+ printf(":%x", hwa[i] & 0xFF);
+ putchar('\n');
+ }
+
+ }
+ exit(0);
+}
diff --git a/libexec/bootpd/tzone.c b/libexec/bootpd/tzone.c
new file mode 100644
index 000000000000..d0c20a389c06
--- /dev/null
+++ b/libexec/bootpd/tzone.c
@@ -0,0 +1,46 @@
+/*
+ * tzone.c - get the timezone
+ *
+ * This is shared by bootpd and bootpef
+ */
+
+#ifdef SVR4
+/* XXX - Is this really SunOS specific? -gwr */
+/* This is in <time.h> but only visible if (__STDC__ == 1). */
+extern long timezone;
+#else /* SVR4 */
+/* BSD or SunOS */
+# include <time.h>
+# include <syslog.h>
+#endif /* SVR4 */
+
+#include "bptypes.h"
+#include "report.h"
+#include "tzone.h"
+
+/* This is what other modules use. */
+int32 secondswest;
+
+/*
+ * Get our timezone offset so we can give it to clients if the
+ * configuration file doesn't specify one.
+ */
+void
+tzone_init()
+{
+#ifdef SVR4
+ /* XXX - Is this really SunOS specific? -gwr */
+ secondswest = timezone;
+#else /* SVR4 */
+ struct tm *tm;
+ time_t now;
+
+ (void)time(&now);
+ if ((tm = localtime(&now)) == NULL) {
+ secondswest = 0; /* Assume GMT for lack of anything better */
+ report(LOG_ERR, "localtime() failed");
+ } else {
+ secondswest = -tm->tm_gmtoff;
+ }
+#endif /* SVR4 */
+}
diff --git a/libexec/bootpd/tzone.h b/libexec/bootpd/tzone.h
new file mode 100644
index 000000000000..ddd67c4b625c
--- /dev/null
+++ b/libexec/bootpd/tzone.h
@@ -0,0 +1,3 @@
+/* tzone.h */
+extern int32 secondswest;
+extern void tzone_init();
diff --git a/libexec/comsat/Makefile b/libexec/comsat/Makefile
new file mode 100644
index 000000000000..3f349abbde22
--- /dev/null
+++ b/libexec/comsat/Makefile
@@ -0,0 +1,4 @@
+PROG= comsat
+MAN= comsat.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/comsat/Makefile.depend b/libexec/comsat/Makefile.depend
new file mode 100644
index 000000000000..6ef78fac5cbf
--- /dev/null
+++ b/libexec/comsat/Makefile.depend
@@ -0,0 +1,15 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/comsat/comsat.8 b/libexec/comsat/comsat.8
new file mode 100644
index 000000000000..a0fde4c53b0b
--- /dev/null
+++ b/libexec/comsat/comsat.8
@@ -0,0 +1,108 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 21, 2010
+.Dt COMSAT 8
+.Os
+.Sh NAME
+.Nm comsat
+.Nd biff server
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility is the server process which receives reports of incoming mail
+and notifies users if they have requested this service.
+The
+.Nm
+utility receives messages on a datagram port associated with the
+.Dq biff
+service
+specification (see
+.Xr services 5
+and
+.Xr inetd 8 ) .
+The one line messages are of the form:
+.Pp
+.D1 Ar user Ns @ Ns Ar mailbox Ns - Ns Ar offset Ns Op : Ns Ar mailbox-name
+.Pp
+If the
+.Ar user
+specified is logged in to the system and the associated terminal has
+the owner execute bit turned on (by a
+.Dq Nm biff Cm y ) ,
+the
+.Ar offset
+is used as a seek offset into the appropriate mailbox file and
+the first 7 lines or 560 characters of the message are printed
+on the user's terminal.
+Lines which appear to be part of
+the message header other than the
+.Dq Li From ,
+.Dq Li \&To ,
+.Dq Li Date ,
+or
+.Dq Li Subject
+lines are not included in the displayed message.
+.Pp
+If the
+.Ar user
+specified is logged in to the system and the associated terminal has
+the group execute bit turned on (by a
+.Dq Nm biff Cm b ) ,
+two bell characters
+.Tn ( ASCII
+\\007) are printed on the user's terminal.
+.Pp
+If
+.Ar mailbox-name
+omitted, standard mailbox assumed.
+.Sh FILES
+.Bl -tag -width ".Pa /var/mail/user" -compact
+.It Pa /var/run/utx.active
+to find out who is logged on and on what terminals
+.It Pa /var/mail/user
+standard mailbox
+.El
+.Sh SEE ALSO
+.Xr biff 1 ,
+.Xr inetd 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+The message header filtering is prone to error.
+The density of the information presented is near the theoretical minimum.
+.Pp
+Users should be notified of mail which arrives on other
+machines than the one to which they are currently logged in.
+.Pp
+The notification should appear in a separate window so it
+does not mess up the screen.
diff --git a/libexec/comsat/comsat.c b/libexec/comsat/comsat.c
new file mode 100644
index 000000000000..cb00ee4a9392
--- /dev/null
+++ b/libexec/comsat/comsat.c
@@ -0,0 +1,264 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <termios.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <utmpx.h>
+
+static int debug = 0;
+#define dsyslog if (debug) syslog
+
+#define MAXIDLE 120
+
+static char hostname[MAXHOSTNAMELEN];
+
+static void jkfprintf(FILE *, char[], off_t);
+static void mailfor(char *);
+static void notify(struct utmpx *, char[], off_t, int);
+static void reapchildren(int);
+
+int
+main(int argc __unused, char *argv[] __unused)
+{
+ struct sockaddr_in from;
+ socklen_t fromlen;
+ int cc;
+ char msgbuf[256];
+
+ /* verify proper invocation */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
+ err(1, "getsockname");
+ openlog("comsat", LOG_PID, LOG_DAEMON);
+ if (chdir(_PATH_MAILDIR)) {
+ syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR);
+ (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
+ exit(1);
+ }
+ (void)gethostname(hostname, sizeof(hostname));
+ (void)signal(SIGTTOU, SIG_IGN);
+ (void)signal(SIGCHLD, reapchildren);
+ for (;;) {
+ cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
+ if (cc <= 0) {
+ if (errno != EINTR)
+ sleep(1);
+ errno = 0;
+ continue;
+ }
+ msgbuf[cc] = '\0';
+ mailfor(msgbuf);
+ sigsetmask(0L);
+ }
+}
+
+static void
+reapchildren(int signo __unused)
+{
+ while (wait3(NULL, WNOHANG, NULL) > 0);
+}
+
+static void
+mailfor(char *name)
+{
+ struct utmpx *utp;
+ char *cp;
+ char *file;
+ off_t offset;
+ int folder;
+ char buf[MAXPATHLEN];
+
+ if ((cp = strchr(name, '@')) == NULL)
+ return;
+ *cp = '\0';
+ offset = strtoll(cp + 1, NULL, 10);
+ if ((cp = strchr(cp + 1, ':')) != NULL &&
+ strchr((file = cp + 1), '/') == NULL) {
+ snprintf(buf, sizeof(buf), "%s/%s", _PATH_MAILDIR, file);
+ folder = 1;
+ } else {
+ snprintf(buf, sizeof(buf), "%s/%s", _PATH_MAILDIR, name);
+ folder = 0;
+ }
+ setutxent();
+ while ((utp = getutxent()) != NULL)
+ if (utp->ut_type == USER_PROCESS && !strcmp(utp->ut_user, name))
+ notify(utp, buf, offset, folder);
+ endutxent();
+}
+
+static const char *cr;
+
+static void
+notify(struct utmpx *utp, char file[], off_t offset, int folder)
+{
+ FILE *tp;
+ struct stat stb;
+ struct termios tio;
+ struct passwd *p;
+ char tty[20];
+ const char *s = utp->ut_line;
+
+ if (strncmp(s, "pts/", 4) == 0)
+ s += 4;
+ if (strchr(s, '/')) {
+ /* A slash is an attempt to break security... */
+ syslog(LOG_AUTH | LOG_NOTICE, "Unexpected `/' in `%s'",
+ utp->ut_line);
+ return;
+ }
+ (void)snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, utp->ut_line);
+ if (stat(tty, &stb) == -1 || !(stb.st_mode & (S_IXUSR | S_IXGRP))) {
+ dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_user, tty);
+ return;
+ }
+ dsyslog(LOG_DEBUG, "notify %s on %s", utp->ut_user, tty);
+ switch (fork()) {
+ case -1:
+ syslog(LOG_NOTICE, "fork failed (%m)");
+ return;
+ case 0:
+ break;
+ default:
+ return;
+ }
+ if ((tp = fopen(tty, "w")) == NULL) {
+ dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno));
+ _exit(1);
+ }
+ (void)tcgetattr(fileno(tp), &tio);
+ cr = ((tio.c_oflag & (OPOST|ONLCR)) == (OPOST|ONLCR)) ? "\n" : "\n\r";
+
+ /* Set uid/gid/groups to user's in case mail drop is on nfs */
+ if ((p = getpwnam(utp->ut_user)) == NULL ||
+ initgroups(p->pw_name, p->pw_gid) == -1 ||
+ setgid(p->pw_gid) == -1 ||
+ setuid(p->pw_uid) == -1)
+ _exit(1);
+
+ if (stb.st_mode & S_IXUSR) {
+ (void)fprintf(tp,
+ "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s",
+ cr, utp->ut_user, (int)sizeof(hostname), hostname,
+ folder ? cr : "", folder ? "to " : "", folder ? file : "",
+ cr, cr);
+ jkfprintf(tp, file, offset);
+ } else if (stb.st_mode & S_IXGRP) {
+ (void)fprintf(tp, "\007");
+ (void)fflush(tp);
+ (void)sleep(1);
+ (void)fprintf(tp, "\007");
+ }
+ (void)fclose(tp);
+ _exit(0);
+}
+
+static void
+jkfprintf(FILE *tp, char file[], off_t offset)
+{
+ unsigned char *cp, ch;
+ FILE *fi;
+ int linecnt, charcnt, inheader;
+ unsigned char line[BUFSIZ];
+
+ if ((fi = fopen(file, "r")) == NULL)
+ return;
+
+ (void)fseeko(fi, offset, SEEK_CUR);
+ /*
+ * Print the first 7 lines or 560 characters of the new mail
+ * (whichever comes first). Skip header crap other than
+ * From, Subject, To, and Date.
+ */
+ linecnt = 7;
+ charcnt = 560;
+ inheader = 1;
+ while (fgets(line, sizeof(line), fi) != NULL) {
+ if (inheader) {
+ if (line[0] == '\n') {
+ inheader = 0;
+ continue;
+ }
+ if (line[0] == ' ' || line[0] == '\t' ||
+ (strncmp(line, "From:", 5) &&
+ strncmp(line, "Subject:", 8)))
+ continue;
+ }
+ if (linecnt <= 0 || charcnt <= 0) {
+ (void)fprintf(tp, "...more...%s", cr);
+ (void)fclose(fi);
+ return;
+ }
+ /* strip weird stuff so can't trojan horse stupid terminals */
+ for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) {
+ /* disable upper controls and enable all other
+ 8bit codes due to lack of locale knowledge
+ */
+ if (((ch & 0x80) && ch < 0xA0) ||
+ (!(ch & 0x80) && !isprint(ch) &&
+ !isspace(ch) && ch != '\a' && ch != '\b')
+ ) {
+ if (ch & 0x80) {
+ ch &= ~0x80;
+ (void)fputs("M-", tp);
+ }
+ if (iscntrl(ch)) {
+ ch ^= 0x40;
+ (void)fputc('^', tp);
+ }
+ }
+ (void)fputc(ch, tp);
+ }
+ (void)fputs(cr, tp);
+ --linecnt;
+ }
+ (void)fprintf(tp, "----%s\n", cr);
+ (void)fclose(fi);
+}
diff --git a/libexec/dma/Makefile b/libexec/dma/Makefile
new file mode 100644
index 000000000000..6150359b2c0b
--- /dev/null
+++ b/libexec/dma/Makefile
@@ -0,0 +1,4 @@
+SUBDIR= dmagent \
+ dma-mbox-create
+
+.include <bsd.subdir.mk>
diff --git a/libexec/dma/Makefile.inc b/libexec/dma/Makefile.inc
new file mode 100644
index 000000000000..e4eb86c4b039
--- /dev/null
+++ b/libexec/dma/Makefile.inc
@@ -0,0 +1,12 @@
+.sinclude "${.CURDIR:H:H}/Makefile.inc"
+DMA_SOURCES= ${SRCTOP}/contrib/dma
+.PATH: ${DMA_SOURCES}
+
+CFLAGS+= -I${DMA_SOURCES} \
+ -DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME \
+ -DCONF_PATH='"/etc/dma"' \
+ -DLIBEXEC_PATH='"/usr/libexec"' -DDMA_VERSION='"v0.13+"' \
+ -DDMA_ROOT_USER='"mailnull"' \
+ -DDMA_GROUP='"mail"'
+BINGRP= mail
+PACKAGE= dma
diff --git a/libexec/dma/dma-mbox-create/Makefile b/libexec/dma/dma-mbox-create/Makefile
new file mode 100644
index 000000000000..26f669e2e0b6
--- /dev/null
+++ b/libexec/dma/dma-mbox-create/Makefile
@@ -0,0 +1,8 @@
+MAN=
+
+WARNS?= 2
+
+PROG= dma-mbox-create
+BINMODE= 4554
+
+.include <bsd.prog.mk>
diff --git a/libexec/dma/dma-mbox-create/Makefile.depend b/libexec/dma/dma-mbox-create/Makefile.depend
new file mode 100644
index 000000000000..f68451c76f1d
--- /dev/null
+++ b/libexec/dma/dma-mbox-create/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcapsicum \
+ lib/libcasper/libcasper \
+ lib/libcompiler_rt \
+ secure/lib/libcrypto \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/dma/dmagent/Makefile b/libexec/dma/dmagent/Makefile
new file mode 100644
index 000000000000..f707cfa3264f
--- /dev/null
+++ b/libexec/dma/dmagent/Makefile
@@ -0,0 +1,38 @@
+.include <src.opts.mk>
+
+LIBADD= ssl crypto
+
+PROG= dma
+SRCS= aliases_parse.y \
+ aliases_scan.l \
+ base64.c \
+ conf.c \
+ crypto.c \
+ dma.c \
+ dns.c \
+ local.c \
+ mail.c \
+ net.c \
+ spool.c \
+ util.c
+MAN= dma.8
+MLINKS= dma.8 dma.conf.5
+CONFSMODE= 0640
+CONFSGRP= mail
+CONFS= auth.conf dma.conf
+CONFSDIR= ${CONFDIR}/dma
+CFLAGS+= -DOPENSSL_API_COMPAT=0x10100000L
+YFLAGS+= -i
+CLEANFILES= aliases_parse.i
+FILES= mailer.conf
+FILESDIR= ${SHAREDIR}/examples/dma
+
+BINMODE= 2555
+
+.include <bsd.compiler.mk>
+
+.if ${COMPILER_TYPE} == gcc
+WARNS?= 5
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/dma/dmagent/Makefile.depend b/libexec/dma/dmagent/Makefile.depend
new file mode 100644
index 000000000000..fad0a57ac11f
--- /dev/null
+++ b/libexec/dma/dmagent/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ secure/lib/libcrypto \
+ secure/lib/libssl \
+ usr.bin/yacc.host \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/dma/dmagent/auth.conf b/libexec/dma/dmagent/auth.conf
new file mode 100644
index 000000000000..393a80a8ab5f
--- /dev/null
+++ b/libexec/dma/dmagent/auth.conf
@@ -0,0 +1,4 @@
+# $DragonFly: src/etc/dma/auth.conf,v 1.1 2008/02/02 18:24:00 matthias Exp $
+#
+# SMTP authentication entries (currently AUTH LOGIN only)
+# Format: user|my.smarthost.example.com:password
diff --git a/libexec/dma/dmagent/dma.conf b/libexec/dma/dmagent/dma.conf
new file mode 100644
index 000000000000..bb28306e342e
--- /dev/null
+++ b/libexec/dma/dmagent/dma.conf
@@ -0,0 +1,63 @@
+#
+# Your smarthost (also called relayhost). Leave blank if you don't want
+# smarthost support.
+#SMARTHOST
+
+# Use this SMTP port. Most users will be fine with the default (25)
+#PORT 25
+
+# Path to your alias file. Just stay with the default.
+#ALIASES /etc/aliases
+
+# Path to your spooldir. Just stay with the default.
+#SPOOLDIR /var/spool/dma
+
+# SMTP authentication
+#AUTHPATH /etc/dma/auth.conf
+
+# Uncomment if you want TLS/SSL support
+#SECURETRANSFER
+
+# Uncomment if you want STARTTLS support (only used in combination with
+# SECURETRANSFER)
+#STARTTLS
+
+# Uncomment if you have specified STARTTLS above and it should be allowed
+# to fail ("opportunistic TLS", use an encrypted connection when available
+# but allow an unencrypted one to servers that do not support it)
+#OPPORTUNISTIC_TLS
+
+# Path to your local SSL certificate
+#CERTFILE
+
+# If you want to use plain text SMTP login without using encryption, change
+# the SECURE entry below to INSECURE. Otherwise plain login will only work
+# over a secure connection. Use this option with caution.
+#SECURE
+
+# Uncomment if you want to defer your mails. This is useful if you are
+# behind a dialup line. You have to submit your mails manually with dma -q
+#DEFER
+
+# Uncomment if you want the bounce message to include the complete original
+# message, not just the headers.
+#FULLBOUNCE
+
+# The internet hostname dma uses to identify the host.
+# If not set or empty, the result of gethostname(2) is used.
+# If MAILNAME is an absolute path to a file, the first line of this file
+# will be used as the hostname.
+#MAILNAME mail.example.net
+
+# Masquerade envelope from addresses with this address/hostname.
+# Use this if mails are not accepted by destination mail servers because
+# your sender domain is invalid.
+# By default, MASQUERADE is not set.
+# Format: MASQUERADE [user@][host]
+# Examples:
+# MASQUERADE john@ on host "hamlet" will send all mails as john@hamlet
+# MASQUERADE percolator will send mails as $username@percolator, e.g. fish@percolator
+# MASQUERADE herb@ert will send all mails as herb@ert
+
+# Directly forward the mail to the SMARTHOST bypassing aliases and local delivery
+#NULLCLIENT
diff --git a/libexec/dma/dmagent/mailer.conf b/libexec/dma/dmagent/mailer.conf
new file mode 100644
index 000000000000..adb645e1fa4d
--- /dev/null
+++ b/libexec/dma/dmagent/mailer.conf
@@ -0,0 +1,9 @@
+#
+# mailer.conf for use with dma(8)
+#
+# If sendmail is configured, an example of mailer.conf that uses sendmail
+# instead can be found in /usr/share/examples/sendmail.
+
+sendmail /usr/libexec/dma
+mailq /usr/libexec/dma
+newaliases /usr/libexec/dma
diff --git a/libexec/fingerd/Makefile b/libexec/fingerd/Makefile
new file mode 100644
index 000000000000..e2fe412df8bc
--- /dev/null
+++ b/libexec/fingerd/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PROG= fingerd
+LIBADD= util
+MAN= fingerd.8
+
+WARNS?= 2
+WFORMAT=0
+
+.if ${MK_BLOCKLIST_SUPPORT} != "no"
+CFLAGS+= -DUSE_BLOCKLIST -I${SRCTOP}/contrib/blocklist/include
+LIBADD+= blocklist
+LDFLAGS+=-L${LIBBLOCKLISTDIR}
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/fingerd/Makefile.depend b/libexec/fingerd/Makefile.depend
new file mode 100644
index 000000000000..bbd6b7071809
--- /dev/null
+++ b/libexec/fingerd/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/fingerd/Makefile.depend.options b/libexec/fingerd/Makefile.depend.options
new file mode 100644
index 000000000000..f68343adae89
--- /dev/null
+++ b/libexec/fingerd/Makefile.depend.options
@@ -0,0 +1,5 @@
+# This file is not autogenerated - take care!
+
+DIRDEPS_OPTIONS= BLOCKLIST_SUPPORT
+
+.include <dirdeps-options.mk>
diff --git a/libexec/fingerd/fingerd.8 b/libexec/fingerd/fingerd.8
new file mode 100644
index 000000000000..29dab8636f22
--- /dev/null
+++ b/libexec/fingerd/fingerd.8
@@ -0,0 +1,158 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd November 19, 2014
+.Dt FINGERD 8
+.Os
+.Sh NAME
+.Nm fingerd
+.Nd remote user information server
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl k
+.Op Fl s
+.Op Fl l
+.Op Fl p Ar filename
+.Sh DESCRIPTION
+The
+.Nm
+utility uses a simple protocol based on
+.%T RFC1196
+that provides an interface to
+.Xr finger 1
+at several network sites.
+It is supposed to return a friendly,
+human-oriented status report on either the system at the moment
+or a particular person in depth.
+There is no required format and the
+protocol consists mostly of specifying a single
+.Dq "command line" ,
+thus,
+.Nm
+can also be used to implement other protocols in conjunction with the
+.Fl p
+flag.
+.Pp
+The
+.Nm
+utility is started by
+.Xr inetd 8 ,
+which listens for
+.Tn TCP
+requests at port 79.
+Once connected it reads a single command line
+terminated by a
+.Aq Tn CRLF
+which is passed to
+.Xr finger 1 .
+The
+.Nm
+utility closes its connections as soon as the output is finished.
+.Pp
+If the line is null (i.e., just a
+.Aq Tn CRLF
+is sent) then
+.Xr finger 1
+returns a
+.Dq default
+report that lists all people logged into
+the system at that moment.
+.Pp
+If a user name is specified (e.g.,\&
+.Pf eric Aq Tn CRLF )
+then the
+response lists more extended information for only that particular user,
+whether logged in or not.
+Allowable
+.Dq names
+in the command line include both
+.Dq login names
+and
+.Dq user names .
+If a name is ambiguous, all possible derivations are returned.
+.Pp
+The following options may be passed to
+.Nm
+as server program arguments in
+.Pa /etc/inetd.conf :
+.Bl -tag -width indent
+.It Fl d
+Enable debugging mode.
+In debugging mode,
+.Nm
+will not attempt any network-related operations on
+.Va stdin ,
+and it will print the full
+.Nm finger
+command line
+to
+.Va stderr
+before executing it.
+.It Fl k
+Suppress login information.
+See the description of the
+.Fl k
+option in
+.Xr finger 1
+for details.
+.It Fl s
+Enable secure mode.
+Queries without a user name are rejected and
+forwarding of queries to other remote hosts is denied.
+.It Fl l
+Enable logging.
+The name of the host originating the query is reported via
+.Xr syslog 3
+at LOG_NOTICE priority.
+.It Fl p
+Use an alternate program as the local information provider.
+The default local program
+executed by
+.Nm
+is
+.Xr finger 1 .
+By specifying a customized local server,
+this option allows a system manager
+to have more control over what information is
+provided to remote sites.
+If
+.Fl p
+is specified,
+.Nm
+will also set the environment variable
+.Ev FINGERD_REMOTE_HOST
+to the name of the host making the request.
+.El
+.Sh SEE ALSO
+.Xr finger 1 ,
+.Xr inetd 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
diff --git a/libexec/fingerd/fingerd.c b/libexec/fingerd/fingerd.c
new file mode 100644
index 000000000000..8b63aa338b0c
--- /dev/null
+++ b/libexec/fingerd/fingerd.c
@@ -0,0 +1,231 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <unistd.h>
+#include <syslog.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pathnames.h"
+#ifdef USE_BLOCKLIST
+#include <blocklist.h>
+#endif
+
+void logerr(const char *, ...) __printflike(1, 2) __dead2;
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ int ch;
+ char *lp;
+ struct sockaddr_storage ss;
+ socklen_t sval;
+ int p[2], debug, kflag, logging, pflag, secure;
+#define ENTRIES 50
+ char **ap, *av[ENTRIES + 1], **comp, line[1024], *prog;
+ char rhost[MAXHOSTNAMELEN];
+
+ prog = _PATH_FINGER;
+ debug = logging = kflag = pflag = secure = 0;
+ openlog("fingerd", LOG_PID | LOG_CONS, LOG_DAEMON);
+ opterr = 0;
+ while ((ch = getopt(argc, argv, "dklp:s")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'k':
+ kflag = 1;
+ break;
+ case 'l':
+ logging = 1;
+ break;
+ case 'p':
+ prog = optarg;
+ pflag = 1;
+ break;
+ case 's':
+ secure = 1;
+ break;
+ case '?':
+ default:
+ logerr("illegal option -- %c", optopt);
+ }
+
+ /*
+ * Enable server-side Transaction TCP.
+ */
+ if (!debug) {
+ int one = 1;
+ if (setsockopt(STDOUT_FILENO, IPPROTO_TCP, TCP_NOPUSH, &one,
+ sizeof one) < 0) {
+ logerr("setsockopt(TCP_NOPUSH) failed: %m");
+ }
+ }
+
+ if (!fgets(line, sizeof(line), stdin))
+ exit(1);
+
+ if (!debug && (logging || pflag)) {
+ sval = sizeof(ss);
+ if (getpeername(0, (struct sockaddr *)&ss, &sval) < 0)
+ logerr("getpeername: %s", strerror(errno));
+ realhostname_sa(rhost, sizeof rhost - 1,
+ (struct sockaddr *)&ss, sval);
+ rhost[sizeof(rhost) - 1] = '\0';
+ if (pflag)
+ setenv("FINGERD_REMOTE_HOST", rhost, 1);
+ }
+
+ if (logging) {
+ char *t;
+ char *end;
+
+ end = memchr(line, 0, sizeof(line));
+ if (end == NULL) {
+ if ((t = malloc(sizeof(line) + 1)) == NULL)
+ logerr("malloc: %s", strerror(errno));
+ memcpy(t, line, sizeof(line));
+ t[sizeof(line)] = 0;
+ } else {
+ if ((t = strdup(line)) == NULL)
+ logerr("strdup: %s", strerror(errno));
+ }
+ for (end = t; *end; end++)
+ if (*end == '\n' || *end == '\r')
+ *end = ' ';
+ syslog(LOG_NOTICE, "query from %s: `%s'", rhost, t);
+ }
+
+ comp = &av[2];
+ av[3] = "--";
+ if (kflag)
+ *comp-- = "-k";
+ for (lp = line, ap = &av[4];;) {
+ *ap = strtok(lp, " \t\r\n");
+ if (!*ap) {
+ if (secure && ap == &av[4]) {
+#ifdef USE_BLOCKLIST
+ blocklist(1, STDIN_FILENO, "nousername");
+#endif
+ puts("must provide username\r\n");
+ exit(1);
+ }
+ break;
+ }
+ if (secure && strchr(*ap, '@')) {
+#ifdef USE_BLOCKLIST
+ blocklist(1, STDIN_FILENO, "noforwarding");
+#endif
+ puts("forwarding service denied\r\n");
+ exit(1);
+ }
+
+ /* RFC742: "/[Ww]" == "-l" */
+ if ((*ap)[0] == '/' && ((*ap)[1] == 'W' || (*ap)[1] == 'w')) {
+ *comp-- = "-l";
+ }
+ else if (++ap == av + ENTRIES) {
+ *ap = NULL;
+ break;
+ }
+ lp = NULL;
+ }
+
+ if ((lp = strrchr(prog, '/')) != NULL)
+ *comp = ++lp;
+ else
+ *comp = prog;
+ if (pipe(p) < 0)
+ logerr("pipe: %s", strerror(errno));
+
+ if (debug) {
+ fprintf(stderr, "%s", prog);
+ for (ap = comp; *ap != NULL; ++ap)
+ fprintf(stderr, " %s", *ap);
+ fprintf(stderr, "\n");
+ }
+
+ switch(vfork()) {
+ case 0:
+ (void)close(p[0]);
+ if (p[1] != STDOUT_FILENO) {
+ (void)dup2(p[1], STDOUT_FILENO);
+ (void)close(p[1]);
+ }
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+
+#ifdef USE_BLOCKLIST
+ blocklist(0, STDIN_FILENO, "success");
+#endif
+ execv(prog, comp);
+ write(STDERR_FILENO, prog, strlen(prog));
+#define MSG ": cannot execute\n"
+ write(STDERR_FILENO, MSG, strlen(MSG));
+#undef MSG
+ _exit(1);
+ case -1:
+ logerr("fork: %s", strerror(errno));
+ }
+ (void)close(p[1]);
+ if (!(fp = fdopen(p[0], "r")))
+ logerr("fdopen: %s", strerror(errno));
+ while ((ch = getc(fp)) != EOF) {
+ if (ch == '\n')
+ putchar('\r');
+ putchar(ch);
+ }
+ exit(0);
+}
+
+#include <stdarg.h>
+
+void
+logerr(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ (void)vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/libexec/fingerd/pathnames.h b/libexec/fingerd/pathnames.h
new file mode 100644
index 000000000000..02eb4eb0ea69
--- /dev/null
+++ b/libexec/fingerd/pathnames.h
@@ -0,0 +1,32 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _PATH_FINGER "/usr/bin/finger"
diff --git a/libexec/flua/Makefile b/libexec/flua/Makefile
new file mode 100644
index 000000000000..f1c46b270ded
--- /dev/null
+++ b/libexec/flua/Makefile
@@ -0,0 +1,73 @@
+.include <src.lua.mk>
+
+PACKAGE= flua
+
+# New flua modules should be added here rather than to SUBDIR so that we can do
+# the right thing for both bootstrap flua and target flua. The former does not
+# do any shared libs, so we just build them all straight into flua itself rather
+# than mucking about with the infrastructure to make them linkable -- thus, why
+# these are all structured to have a Makefile that describes what we want
+# *installed*, and a Makefile.inc that describes what we need to *build*.
+FLUA_MODULES+= lfbsd
+FLUA_MODULES+= lfs
+FLUA_MODULES+= libhash
+.ifndef BOOTSTRAPPING
+# Bootstrap flua can't usefully do anything with libjail anyways, because it
+# can't assume it's being run on a system that even supports jails.
+FLUA_MODULES+= libjail
+.endif
+FLUA_MODULES+= libucl
+FLUA_MODULES+= liblyaml
+
+.ifdef BOOTSTRAPPING
+# libfreebsd is generally omitted from the bootstrap flua because its
+# functionality largely assumes a FreeBSD kernel/system headers, so it doesn't
+# really offer functionality that we can use in bootstrap.
+CFLAGS+= -I${.CURDIR} -DBOOTSTRAPPING
+
+SHAREDIR= ${WORLDTMP}/legacy/usr/share/flua
+FLUA_PATH= ${SHAREDIR}/?.lua;${SHAREDIR}/?/init.lua
+CFLAGS+= -DBOOTSTRAP_FLUA_PATH=\"${FLUA_PATH:Q}\"
+
+.for mod in ${FLUA_MODULES}
+.include "${mod}/Makefile.inc"
+.endfor
+
+.else
+
+FLUA_MODULES+= libfreebsd
+SUBDIR+= ${FLUA_MODULES}
+
+.endif
+
+LUASRC?= ${SRCTOP}/contrib/lua/src
+.PATH: ${LUASRC}
+
+PROG= flua
+WARNS?= 3
+
+CWARNFLAGS.gcc+= -Wno-format-nonliteral
+
+LIBADD+= lua
+
+# Entry point
+SRCS+= lua.c
+
+# FreeBSD Extensions
+.PATH: ${.CURDIR}/modules
+SRCS+= linit_flua.c
+SRCS+= lposix.c
+
+CFLAGS+= -I${SRCTOP}/lib/liblua -I${.CURDIR}/modules -I${LUASRC}
+CFLAGS+= -DLUA_PROGNAME="\"${PROG}\""
+
+# readline bits; these aren't needed if we're building a bootstrap flua, as we
+# don't expect that one to see any REPL usage.
+.if !defined(BOOTSTRAPPING)
+CFLAGS+= -DLUA_USE_READLINE
+CFLAGS+= -I${SRCTOP}/lib/libedit -I${SRCTOP}/contrib/libedit
+LIBADD+= edit
+LDFLAGS+= -Wl,-E
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/flua/Makefile.inc b/libexec/flua/Makefile.inc
new file mode 100644
index 000000000000..5e214c76921b
--- /dev/null
+++ b/libexec/flua/Makefile.inc
@@ -0,0 +1,12 @@
+PACKAGE= flua
+
+SHLIBDIR?= ${LIBDIR}/flua
+
+CFLAGS+= \
+ -I${SRCTOP}/contrib/lua/src \
+ -I${SRCTOP}/lib/liblua \
+ -I${SRCTOP}/libexec/flua
+
+.ifdef BOOTSTRAPPING
+CFLAGS+= -DBOOTSTRAPPING
+.endif
diff --git a/libexec/flua/bootstrap.h b/libexec/flua/bootstrap.h
new file mode 100644
index 000000000000..caf00518c1e0
--- /dev/null
+++ b/libexec/flua/bootstrap.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2025 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef FLUA_BOOTSTRAP_H
+#define FLUA_BOOTSTRAP_H
+
+#ifdef BOOTSTRAPPING
+#include <sys/linker_set.h>
+
+#include <lauxlib.h>
+
+#define FLUA_MODULE_SETNAME flua_modules
+
+SET_DECLARE(FLUA_MODULE_SETNAME, const luaL_Reg);
+#define FLUA_MODULE_DEF(ident, modname, openfn) \
+ static const luaL_Reg ident = { modname, openfn }; \
+ DATA_SET(FLUA_MODULE_SETNAME, ident)
+
+#define FLUA_MODULE_NAMED(mod, name) \
+ FLUA_MODULE_DEF(module_ ## mod, name, luaopen_ ## mod)
+#define FLUA_MODULE(mod) \
+ FLUA_MODULE_DEF(module_ ## mod, #mod, luaopen_ ## mod)
+#else /* !BOOTSTRAPPING */
+#define FLUA_MODULE_DEF(ident, modname, openfn)
+#define FLUA_MODULE_NAMED(mod, name)
+#define FLUA_MODULE(modname)
+#endif /* BOOTSTRAPPING */
+
+#endif /* FLUA_BOOTSTRAP_H */
diff --git a/libexec/flua/flua.1 b/libexec/flua/flua.1
new file mode 100644
index 000000000000..a315e4106065
--- /dev/null
+++ b/libexec/flua/flua.1
@@ -0,0 +1,99 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2025 The FreeBSD Foundation
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd April 28, 2025
+.Dt FLUA 1
+.Os
+.Sh NAME
+.Nm flua
+.Nd Lua interpreter for the FreeBSD base system
+.Sh SYNOPSIS
+.Nm flua
+.Op Fl EWiv
+.Op Fl e Ar string
+.Op Fl l Ar module
+.Op Fl l Ar g=module
+.Op Ar script Op Ar args
+.Op Fl -
+.Op Fl
+.Sh DESCRIPTION
+.Nm
+is a minimal Lua interpreter integrated into the FreeBSD base system.
+It is derived from Lua 5.4 with modifications to suit the needs of
+.Fx
+build infrastructure and system tooling.
+.Nm
+is intended for internal use within the base system and is
+.Em not
+designed for general-purpose scripting or use by third-party applications.
+.Pp
+Unlike full Lua installations provided by the Ports Collection,
+.Nm
+has a reduced feature set and is limited to meeting the requirements of
+base system environments such as the bootloader.
+.Sh USAGE
+.Nm
+is typically invoked internally by FreeBSD base system tools and build scripts.
+While it accepts Lua source files and arguments in a standard fashion, its
+limited environment and module support make it unsuitable for general scripting
+use.
+.Sh INCLUDED MODULES
+.Nm
+includes a subset of functionality from a small number of standard Lua modules
+as well as bespoke modules necessary for the base system:
+.Bl -bullet
+.It
+lfs (LuaFileSystem) – file attribute and directory manipulation
+.It
+lposix - basic POSIX system calls
+.It
+.Xr freebsd.kenv 3lua
+.It
+.Xr freebsd.sys.linker 3lua
+.It
+.Xr hash 3lua
+.It
+.Xr jail 3lua
+.El
+.Sh NOTES
+.Nm
+should not be used as a replacement for
+.Xr lua 1
+from the Ports Collection (e.g.,
+.Pa lang/lua54 )
+as it may be modified or updated to a newer Lua version in the future without
+retaining backwards compatibility.
+.Sh SEE ALSO
+.Xr freebsd.kenv 3lua ,
+.Xr freebsd.sys.linker 3lua ,
+.Xr hash 3lua ,
+.Xr jail 3lua ,
+.Xr style.lua 9
+.Sh HISTORY
+.Nm
+first appeared in
+.Fx 14.0 .
diff --git a/libexec/flua/lfbsd/Makefile b/libexec/flua/lfbsd/Makefile
new file mode 100644
index 000000000000..e2a4aae14bcd
--- /dev/null
+++ b/libexec/flua/lfbsd/Makefile
@@ -0,0 +1,5 @@
+SHLIB_NAME= fbsd.so
+WARNS?= 3
+
+.include "Makefile.inc"
+.include <bsd.lib.mk>
diff --git a/libexec/flua/lfbsd/Makefile.inc b/libexec/flua/lfbsd/Makefile.inc
new file mode 100644
index 000000000000..7a78ef82e0fc
--- /dev/null
+++ b/libexec/flua/lfbsd/Makefile.inc
@@ -0,0 +1,2 @@
+.PATH: ${.PARSEDIR}
+SRCS+= lfbsd.c
diff --git a/libexec/flua/lfbsd/lfbsd.c b/libexec/flua/lfbsd/lfbsd.c
new file mode 100644
index 000000000000..541b6c9611df
--- /dev/null
+++ b/libexec/flua/lfbsd/lfbsd.c
@@ -0,0 +1,289 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2023 Baptiste Daroussin <bapt@FreeBSD.org>
+ * Copyright (C) 2025 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions~
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <spawn.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <lua.h>
+#include "lauxlib.h"
+#include "lfbsd.h"
+
+#include "bootstrap.h"
+
+#define FBSD_PROCESSHANDLE "fbsd_process_t*"
+
+struct fbsd_process {
+ int pid;
+ int stdin_fileno;
+ int stdout_fileno;
+};
+
+extern char **environ;
+
+static const char**
+luaL_checkarraystrings(lua_State *L, int arg)
+{
+ const char **ret;
+ lua_Integer n, i;
+ int t;
+ int abs_arg = lua_absindex(L, arg);
+ luaL_checktype(L, abs_arg, LUA_TTABLE);
+ n = lua_rawlen(L, abs_arg);
+ ret = lua_newuserdata(L, (n+1)*sizeof(char*));
+ for (i=0; i<n; i++) {
+ t = lua_rawgeti(L, abs_arg, i+1);
+ if (t == LUA_TNIL)
+ break;
+ luaL_argcheck(L, t == LUA_TSTRING, arg, "expected array of strings");
+ ret[i] = lua_tostring(L, -1);
+ lua_pop(L, 1);
+ }
+ ret[i] = NULL;
+ return ret;
+}
+
+static void
+close_pipes(int pipes[2])
+{
+
+ if (pipes[0] != -1)
+ close(pipes[0]);
+ if (pipes[1] != -1)
+ close(pipes[1]);
+}
+
+static int
+lua_exec(lua_State *L)
+{
+ struct fbsd_process *proc;
+ int r;
+ posix_spawn_file_actions_t action;
+ int stdin_pipe[2] = {-1, -1};
+ int stdout_pipe[2] = {-1, -1};
+ pid_t pid;
+ const char **argv;
+ int n = lua_gettop(L);
+ bool capture_stdout;
+ luaL_argcheck(L, n > 0 && n <= 2, n >= 2 ? 2 : n,
+ "fbsd.exec takes exactly one or two arguments");
+
+ capture_stdout = lua_toboolean(L, 2);
+ if (pipe(stdin_pipe) < 0) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+ if (capture_stdout && pipe(stdout_pipe) < 0) {
+ close_pipes(stdin_pipe);
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+
+ proc = lua_newuserdata(L, sizeof(*proc));
+ proc->stdin_fileno = stdin_pipe[1];
+ proc->stdout_fileno = stdout_pipe[1];
+ posix_spawn_file_actions_init(&action);
+ posix_spawn_file_actions_adddup2(&action, stdin_pipe[0], STDIN_FILENO);
+ posix_spawn_file_actions_addclose(&action, stdin_pipe[1]);
+ if (stdin_pipe[0] != STDIN_FILENO)
+ posix_spawn_file_actions_addclose(&action, stdin_pipe[0]);
+
+ /*
+ * Setup stdout to be captured if requested. Otherwise, we just let it
+ * go to our own stdout.
+ */
+ if (stdout_pipe[0] != -1) {
+ posix_spawn_file_actions_adddup2(&action, stdout_pipe[0],
+ STDOUT_FILENO);
+ posix_spawn_file_actions_addclose(&action, stdout_pipe[1]);
+ if (stdout_pipe[0] != STDOUT_FILENO) {
+ posix_spawn_file_actions_addclose(&action,
+ stdout_pipe[0]);
+ }
+ }
+
+ argv = luaL_checkarraystrings(L, 1);
+ if (0 != (r = posix_spawnp(&pid, argv[0], &action, NULL,
+ (char*const*)argv, environ))) {
+ close_pipes(stdin_pipe);
+ close_pipes(stdout_pipe);
+ posix_spawn_file_actions_destroy(&action);
+ lua_pop(L, 2); /* Pop off the process handle and args. */
+
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(r));
+ lua_pushinteger(L, r);
+ return (3);
+ }
+
+ lua_pop(L, 1);
+
+ close(stdin_pipe[0]);
+ if (stdout_pipe[0] != -1)
+ close(stdout_pipe[0]);
+ posix_spawn_file_actions_destroy(&action);
+
+ proc->pid = pid;
+ luaL_setmetatable(L, FBSD_PROCESSHANDLE);
+
+ return (1);
+}
+
+static int
+lua_process_close(lua_State *L)
+{
+ struct fbsd_process *proc;
+ int pstat, r;
+
+ proc = luaL_checkudata(L, 1, FBSD_PROCESSHANDLE);
+ while (waitpid(proc->pid, &pstat, 0) == -1) {
+ if ((r = errno) != EINTR) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(r));
+ lua_pushinteger(L, r);
+ return (3);
+ }
+ }
+
+ if (!WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) {
+ lua_pushnil(L);
+ lua_pushstring(L, "Abnormal termination");
+ return (2);
+ }
+
+ if (proc->stdin_fileno >= 0) {
+ close(proc->stdin_fileno);
+ proc->stdin_fileno = -1;
+ }
+
+ if (proc->stdout_fileno >= 0) {
+ close(proc->stdout_fileno);
+ proc->stdout_fileno = -1;
+ }
+
+ lua_pushboolean(L, 1);
+ return (1);
+}
+
+static int
+lua_process_makestdio(lua_State *L, int fd, const char *mode)
+{
+ luaL_Stream *p;
+ FILE *fp;
+ int r;
+
+ if (fd == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, "Stream not captured");
+ return (2);
+ }
+
+ fp = fdopen(fd, mode);
+ if (fp == NULL) {
+ r = errno;
+
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(r));
+ lua_pushinteger(L, r);
+ return (3);
+ }
+
+ p = lua_newuserdata(L, sizeof(*p));
+ p->closef = &lua_process_close;
+ p->f = fp;
+ luaL_setmetatable(L, LUA_FILEHANDLE);
+ return (1);
+}
+
+static int
+lua_process_stdin(lua_State *L)
+{
+ struct fbsd_process *proc;
+
+ proc = luaL_checkudata(L, 1, FBSD_PROCESSHANDLE);
+ return (lua_process_makestdio(L, proc->stdin_fileno, "w"));
+}
+
+static int
+lua_process_stdout(lua_State *L)
+{
+ struct fbsd_process *proc;
+
+ proc = luaL_checkudata(L, 1, FBSD_PROCESSHANDLE);
+ return (lua_process_makestdio(L, proc->stdout_fileno, "r"));
+}
+
+#define PROCESS_SIMPLE(n) { #n, lua_process_ ## n }
+static const struct luaL_Reg fbsd_process[] = {
+ PROCESS_SIMPLE(close),
+ PROCESS_SIMPLE(stdin),
+ PROCESS_SIMPLE(stdout),
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg fbsd_process_meta[] = {
+ { "__index", NULL },
+ { "__gc", lua_process_close },
+ { "__close", lua_process_close },
+ { NULL, NULL },
+};
+
+#define REG_SIMPLE(n) { #n, lua_ ## n }
+static const struct luaL_Reg fbsd_lib[] = {
+ REG_SIMPLE(exec),
+ { NULL, NULL },
+};
+#undef REG_SIMPLE
+
+int
+luaopen_fbsd(lua_State *L)
+{
+ luaL_newlib(L, fbsd_lib);
+
+ luaL_newmetatable(L, FBSD_PROCESSHANDLE);
+ luaL_setfuncs(L, fbsd_process_meta, 0);
+
+ luaL_newlibtable(L, fbsd_process);
+ luaL_setfuncs(L, fbsd_process, 0);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+
+ return (1);
+}
+
+FLUA_MODULE(fbsd);
diff --git a/libexec/flua/lfbsd/lfbsd.h b/libexec/flua/lfbsd/lfbsd.h
new file mode 100644
index 000000000000..01034a3ad7cd
--- /dev/null
+++ b/libexec/flua/lfbsd/lfbsd.h
@@ -0,0 +1,32 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2023 Baptiste Daroussin <bapt@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions~
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <lua.h>
+
+int luaopen_fbsd(lua_State *L);
diff --git a/libexec/flua/lfs/Makefile b/libexec/flua/lfs/Makefile
new file mode 100644
index 000000000000..3df83d6d2fc1
--- /dev/null
+++ b/libexec/flua/lfs/Makefile
@@ -0,0 +1,5 @@
+SHLIB_NAME= lfs.so
+WARNS?= 3
+
+.include "Makefile.inc"
+.include <bsd.lib.mk>
diff --git a/libexec/flua/lfs/Makefile.inc b/libexec/flua/lfs/Makefile.inc
new file mode 100644
index 000000000000..9d40c42dc0e6
--- /dev/null
+++ b/libexec/flua/lfs/Makefile.inc
@@ -0,0 +1,2 @@
+.PATH: ${.PARSEDIR}
+SRCS+= lfs.c
diff --git a/libexec/flua/lfs/lfs.c b/libexec/flua/lfs/lfs.c
new file mode 100644
index 000000000000..517e16ae65c8
--- /dev/null
+++ b/libexec/flua/lfs/lfs.c
@@ -0,0 +1,453 @@
+/*-
+ * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Portions derived from https://github.com/keplerproject/luafilesystem under
+ * the terms of the MIT license:
+ *
+ * Copyright (c) 2003-2014 Kepler Project.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef _STANDALONE
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#endif
+
+#include <lua.h>
+#include "lauxlib.h"
+#include "lfs.h"
+
+#ifdef _STANDALONE
+#include "lstd.h"
+#include "lutils.h"
+#endif
+
+#include "bootstrap.h"
+
+#ifndef nitems
+#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
+#endif
+
+/*
+ * The goal is to emulate a subset of the upstream Lua FileSystem library, as
+ * faithfully as possible in the boot environment. Only APIs that seem useful
+ * need to emulated.
+ *
+ * Example usage:
+ *
+ * for file in lfs.dir("/boot") do
+ * print("\t"..file)
+ * end
+ *
+ * Prints:
+ * .
+ * ..
+ * (etc.)
+ *
+ * The other available API is lfs.attributes(), which functions somewhat like
+ * stat(2) and returns a table of values. Example code:
+ *
+ * attrs, errormsg, errorcode = lfs.attributes("/boot")
+ * if attrs == nil then
+ * print(errormsg)
+ * return errorcode
+ * end
+ *
+ * for k, v in pairs(attrs) do
+ * print(k .. ":\t" .. v)
+ * end
+ * return 0
+ *
+ * Prints (on success):
+ * gid: 0
+ * change: 140737488342640
+ * mode: directory
+ * rdev: 0
+ * ino: 4199275
+ * dev: 140737488342544
+ * modification: 140737488342576
+ * size: 512
+ * access: 140737488342560
+ * permissions: 755
+ * nlink: 58283552
+ * uid: 1001
+ */
+
+#define DIR_METATABLE "directory iterator metatable"
+
+static int
+lua_dir_iter_pushtype(lua_State *L __unused, const struct dirent *ent __unused)
+{
+
+ /*
+ * This is a non-standard extension to luafilesystem for loader's
+ * benefit. The extra stat() calls to determine the entry type can
+ * be quite expensive on some systems, so this speeds up enumeration of
+ * /boot greatly by providing the type up front.
+ *
+ * This extension is compatible enough with luafilesystem, in that we're
+ * just using an extra return value for the iterator.
+ */
+#ifdef _STANDALONE
+ lua_pushinteger(L, ent->d_type);
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static int
+lua_dir_iter_next(lua_State *L)
+{
+ struct dirent *entry;
+ DIR *dp, **dpp;
+
+ dpp = (DIR **)luaL_checkudata(L, 1, DIR_METATABLE);
+ dp = *dpp;
+ luaL_argcheck(L, dp != NULL, 1, "closed directory");
+
+#ifdef _STANDALONE
+ entry = readdirfd(dp->fd);
+#else
+ entry = readdir(dp);
+#endif
+ if (entry == NULL) {
+ closedir(dp);
+ *dpp = NULL;
+ return 0;
+ }
+
+ lua_pushstring(L, entry->d_name);
+ return 1 + lua_dir_iter_pushtype(L, entry);
+}
+
+static int
+lua_dir_iter_close(lua_State *L)
+{
+ DIR *dp, **dpp;
+
+ dpp = (DIR **)lua_touserdata(L, 1);
+ dp = *dpp;
+ if (dp == NULL)
+ return 0;
+
+ closedir(dp);
+ *dpp = NULL;
+ return 0;
+}
+
+static int
+lua_dir(lua_State *L)
+{
+ const char *path;
+ DIR *dp;
+
+ if (lua_gettop(L) != 1) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ path = luaL_checkstring(L, 1);
+ dp = opendir(path);
+ if (dp == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ lua_pushcfunction(L, lua_dir_iter_next);
+ *(DIR **)lua_newuserdata(L, sizeof(DIR **)) = dp;
+ luaL_getmetatable(L, DIR_METATABLE);
+ lua_setmetatable(L, -2);
+ return 2;
+}
+
+static void
+register_metatable(lua_State *L)
+{
+ /*
+ * Create so-called metatable for iterator object returned by
+ * lfs.dir().
+ */
+ luaL_newmetatable(L, DIR_METATABLE);
+
+ lua_newtable(L);
+ lua_pushcfunction(L, lua_dir_iter_next);
+ lua_setfield(L, -2, "next");
+ lua_pushcfunction(L, lua_dir_iter_close);
+ lua_setfield(L, -2, "close");
+
+ /* Magically associate anonymous method table with metatable. */
+ lua_setfield(L, -2, "__index");
+ /* Implement magic destructor method */
+ lua_pushcfunction(L, lua_dir_iter_close);
+ lua_setfield(L, -2, "__gc");
+
+ lua_pop(L, 1);
+}
+
+#define PUSH_INTEGER(lname, stname) \
+static void \
+push_st_ ## lname (lua_State *L, struct stat *sb) \
+{ \
+ lua_pushinteger(L, (lua_Integer)sb->st_ ## stname); \
+}
+PUSH_INTEGER(dev, dev)
+PUSH_INTEGER(ino, ino)
+PUSH_INTEGER(nlink, nlink)
+PUSH_INTEGER(uid, uid)
+PUSH_INTEGER(gid, gid)
+PUSH_INTEGER(rdev, rdev)
+PUSH_INTEGER(access, atime)
+PUSH_INTEGER(modification, mtime)
+PUSH_INTEGER(change, ctime)
+PUSH_INTEGER(size, size)
+#undef PUSH_INTEGER
+
+static void
+push_st_mode(lua_State *L, struct stat *sb)
+{
+ const char *mode_s;
+ mode_t mode;
+
+ mode = (sb->st_mode & S_IFMT);
+ if (S_ISREG(mode))
+ mode_s = "file";
+ else if (S_ISDIR(mode))
+ mode_s = "directory";
+ else if (S_ISLNK(mode))
+ mode_s = "link";
+ else if (S_ISSOCK(mode))
+ mode_s = "socket";
+ else if (S_ISFIFO(mode))
+ mode_s = "fifo";
+ else if (S_ISCHR(mode))
+ mode_s = "char device";
+ else if (S_ISBLK(mode))
+ mode_s = "block device";
+ else
+ mode_s = "other";
+
+ lua_pushstring(L, mode_s);
+}
+
+static void
+push_st_permissions(lua_State *L, struct stat *sb)
+{
+ char buf[20];
+
+ /*
+ * XXX
+ * Could actually format as "-rwxrwxrwx" -- do we care?
+ */
+ snprintf(buf, sizeof(buf), "%o", sb->st_mode & ~S_IFMT);
+ lua_pushstring(L, buf);
+}
+
+#define PUSH_ENTRY(n) { #n, push_st_ ## n }
+struct stat_members {
+ const char *name;
+ void (*push)(lua_State *, struct stat *);
+} members[] = {
+ PUSH_ENTRY(mode),
+ PUSH_ENTRY(dev),
+ PUSH_ENTRY(ino),
+ PUSH_ENTRY(nlink),
+ PUSH_ENTRY(uid),
+ PUSH_ENTRY(gid),
+ PUSH_ENTRY(rdev),
+ PUSH_ENTRY(access),
+ PUSH_ENTRY(modification),
+ PUSH_ENTRY(change),
+ PUSH_ENTRY(size),
+ PUSH_ENTRY(permissions),
+};
+#undef PUSH_ENTRY
+
+static int
+lua_attributes(lua_State *L)
+{
+ struct stat sb;
+ const char *path, *member;
+ size_t i;
+ int rc;
+
+ path = luaL_checkstring(L, 1);
+ if (path == NULL) {
+ lua_pushnil(L);
+ lua_pushfstring(L, "cannot convert first argument to string");
+ lua_pushinteger(L, EINVAL);
+ return 3;
+ }
+
+ rc = stat(path, &sb);
+ if (rc != 0) {
+ lua_pushnil(L);
+ lua_pushfstring(L,
+ "cannot obtain information from file '%s': %s", path,
+ strerror(errno));
+ lua_pushinteger(L, errno);
+ return 3;
+ }
+
+ if (lua_isstring(L, 2)) {
+ member = lua_tostring(L, 2);
+ for (i = 0; i < nitems(members); i++) {
+ if (strcmp(members[i].name, member) != 0)
+ continue;
+
+ members[i].push(L, &sb);
+ return 1;
+ }
+ return luaL_error(L, "invalid attribute name '%s'", member);
+ }
+
+ /* Create or reuse existing table */
+ lua_settop(L, 2);
+ if (!lua_istable(L, 2))
+ lua_newtable(L);
+
+ /* Export all stat data to caller */
+ for (i = 0; i < nitems(members); i++) {
+ lua_pushstring(L, members[i].name);
+ members[i].push(L, &sb);
+ lua_rawset(L, -3);
+ }
+ return 1;
+}
+
+#ifndef _STANDALONE
+#define lfs_mkdir_impl(path) (mkdir((path), \
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | \
+ S_IROTH | S_IXOTH))
+
+static int
+lua_mkdir(lua_State *L)
+{
+ const char *path;
+ int error, serrno;
+
+ path = luaL_checkstring(L, 1);
+ if (path == NULL) {
+ lua_pushnil(L);
+ lua_pushfstring(L, "cannot convert first argument to string");
+ lua_pushinteger(L, EINVAL);
+ return 3;
+ }
+
+ error = lfs_mkdir_impl(path);
+ if (error == -1) {
+ /* Save it; unclear what other libc functions may be invoked */
+ serrno = errno;
+ lua_pushnil(L);
+ lua_pushfstring(L, strerror(serrno));
+ lua_pushinteger(L, serrno);
+ return 3;
+ }
+
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
+static int
+lua_rmdir(lua_State *L)
+{
+ const char *path;
+ int error, serrno;
+
+ path = luaL_checkstring(L, 1);
+ if (path == NULL) {
+ lua_pushnil(L);
+ lua_pushfstring(L, "cannot convert first argument to string");
+ lua_pushinteger(L, EINVAL);
+ return 3;
+ }
+
+ error = rmdir(path);
+ if (error == -1) {
+ /* Save it; unclear what other libc functions may be invoked */
+ serrno = errno;
+ lua_pushnil(L);
+ lua_pushfstring(L, strerror(serrno));
+ lua_pushinteger(L, serrno);
+ return 3;
+ }
+
+ lua_pushboolean(L, 1);
+ return 1;
+}
+#endif
+
+#define REG_SIMPLE(n) { #n, lua_ ## n }
+static const struct luaL_Reg fslib[] = {
+ REG_SIMPLE(attributes),
+ REG_SIMPLE(dir),
+#ifndef _STANDALONE
+ REG_SIMPLE(mkdir),
+ REG_SIMPLE(rmdir),
+#endif
+ { NULL, NULL },
+};
+#undef REG_SIMPLE
+
+int
+luaopen_lfs(lua_State *L)
+{
+ register_metatable(L);
+ luaL_newlib(L, fslib);
+#ifdef _STANDALONE
+ /* Non-standard extension for loader, used with lfs.dir(). */
+ lua_pushinteger(L, DT_DIR);
+ lua_setfield(L, -2, "DT_DIR");
+#endif
+ return 1;
+}
+
+#ifndef _STANDALONE
+FLUA_MODULE(lfs);
+#endif
diff --git a/libexec/flua/lfs/lfs.h b/libexec/flua/lfs/lfs.h
new file mode 100644
index 000000000000..a99e66d7f601
--- /dev/null
+++ b/libexec/flua/lfs/lfs.h
@@ -0,0 +1,31 @@
+/*-
+ * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <lua.h>
+
+int luaopen_lfs(lua_State *L);
diff --git a/libexec/flua/libfreebsd/Makefile b/libexec/flua/libfreebsd/Makefile
new file mode 100644
index 000000000000..36d39d6b0502
--- /dev/null
+++ b/libexec/flua/libfreebsd/Makefile
@@ -0,0 +1,4 @@
+SUBDIR+= kenv
+SUBDIR+= sys
+
+.include <bsd.subdir.mk>
diff --git a/libexec/flua/libfreebsd/Makefile.inc b/libexec/flua/libfreebsd/Makefile.inc
new file mode 100644
index 000000000000..26a1540482c7
--- /dev/null
+++ b/libexec/flua/libfreebsd/Makefile.inc
@@ -0,0 +1,3 @@
+SHLIBDIR?= ${LIBDIR}/flua/freebsd
+
+.include "../Makefile.inc"
diff --git a/libexec/flua/libfreebsd/kenv/Makefile b/libexec/flua/libfreebsd/kenv/Makefile
new file mode 100644
index 000000000000..a1b388bb3612
--- /dev/null
+++ b/libexec/flua/libfreebsd/kenv/Makefile
@@ -0,0 +1,5 @@
+SHLIB_NAME= kenv.so
+MAN= freebsd.kenv.3lua
+
+.include "Makefile.inc"
+.include <bsd.lib.mk>
diff --git a/libexec/flua/libfreebsd/kenv/Makefile.inc b/libexec/flua/libfreebsd/kenv/Makefile.inc
new file mode 100644
index 000000000000..05819c5280d9
--- /dev/null
+++ b/libexec/flua/libfreebsd/kenv/Makefile.inc
@@ -0,0 +1,2 @@
+.PATH: ${.PARSEDIR}
+SRCS+= kenv.c
diff --git a/libexec/flua/libfreebsd/kenv/freebsd.kenv.3lua b/libexec/flua/libfreebsd/kenv/freebsd.kenv.3lua
new file mode 100644
index 000000000000..d254dd22c91c
--- /dev/null
+++ b/libexec/flua/libfreebsd/kenv/freebsd.kenv.3lua
@@ -0,0 +1,44 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2024, Baptiste Daroussin <bapt@FreeBSD.org>
+.\"
+.Dd September 6, 2024
+.Dt FREEBSD.KENV 3lua
+.Os
+.Sh NAME
+.Nm freebsd.kenv
+.Nd Lua binding to
+.Fx 's
+Linker functions
+.Sh SYNOPSIS
+.Bd -literal
+local kenv = require('freebsd.kenv')
+.Ed
+.Pp
+.Bl -tag -width XXXX -compact
+.It Dv table = kenv.get()
+.It Dv value = kenv.get(key)
+.El
+.Sh DESCRIPTION
+The
+.Nm
+module is a binding to the
+.Xr kenv 2
+function.
+.Pp
+List of functions:
+.Bl -tag -width XXXX
+.It Dv table = freebsd.kenv.get()
+Dump the kernel environnement into a key/value
+.Fa table .
+.It Dv value = freebsd.kenv.get(key)
+Return the
+.Fa value
+associated to the
+.Fa key ,
+if it exists, or
+.Va nil
+otherwise.
+.Sh SEE ALSO
+.Xr kenv 2
diff --git a/libexec/flua/libfreebsd/kenv/kenv.c b/libexec/flua/libfreebsd/kenv/kenv.c
new file mode 100644
index 000000000000..56b24c72904a
--- /dev/null
+++ b/libexec/flua/libfreebsd/kenv/kenv.c
@@ -0,0 +1,100 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024, Baptiste Daroussin <bapt@FreeBSD.org>
+ */
+
+#include <errno.h>
+#include <kenv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "bootstrap.h"
+
+int luaopen_freebsd_kenv(lua_State *L);
+
+static int
+lua_kenv_get(lua_State *L)
+{
+ const char *env;
+ int ret, n;
+ char value[1024];
+
+ n = lua_gettop(L);
+ if (n == 0) {
+ char *buf, *bp, *cp;
+ int size;
+
+ size = kenv(KENV_DUMP, NULL, NULL, 0);
+ if (size < 0) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+ size += 1;
+ buf = malloc(size);
+ if (buf == NULL) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+ if (kenv(KENV_DUMP, NULL, buf, size) < 0) {
+ free(buf);
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+
+ lua_newtable(L);
+ for (bp = buf; *bp != '\0'; bp += strlen(bp) + 1) {
+ cp = strchr(bp, '=');
+ if (cp == NULL)
+ continue;
+ *cp++ = '\0';
+ lua_pushstring(L, cp);
+ lua_setfield(L, -2, bp);
+ bp = cp;
+ }
+ free(buf);
+ return (1);
+ }
+ env = luaL_checkstring(L, 1);
+ ret = kenv(KENV_GET, env, value, sizeof(value));
+ if (ret == -1) {
+ if (errno == ENOENT) {
+ lua_pushnil(L);
+ return (1);
+ }
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+ lua_pushstring(L, value);
+ return (1);
+}
+
+#define REG_SIMPLE(n) { #n, lua_kenv_ ## n }
+static const struct luaL_Reg freebsd_kenv[] = {
+ REG_SIMPLE(get),
+ { NULL, NULL },
+};
+#undef REG_SIMPLE
+
+int
+luaopen_freebsd_kenv(lua_State *L)
+{
+ luaL_newlib(L, freebsd_kenv);
+
+ return (1);
+}
+
+FLUA_MODULE_NAMED(freebsd_kenv, "freebsd.kenv");
diff --git a/libexec/flua/libfreebsd/sys/Makefile b/libexec/flua/libfreebsd/sys/Makefile
new file mode 100644
index 000000000000..9f38294536f2
--- /dev/null
+++ b/libexec/flua/libfreebsd/sys/Makefile
@@ -0,0 +1,4 @@
+SUBDIR+= linker
+
+.include <bsd.subdir.mk>
+
diff --git a/libexec/flua/libfreebsd/sys/Makefile.inc b/libexec/flua/libfreebsd/sys/Makefile.inc
new file mode 100644
index 000000000000..ca2630721733
--- /dev/null
+++ b/libexec/flua/libfreebsd/sys/Makefile.inc
@@ -0,0 +1,3 @@
+SHLIBDIR?= ${LIBDIR}/flua/freebsd/sys
+
+.include "../Makefile.inc"
diff --git a/libexec/flua/libfreebsd/sys/linker/Makefile b/libexec/flua/libfreebsd/sys/linker/Makefile
new file mode 100644
index 000000000000..f1f65ad5f6c1
--- /dev/null
+++ b/libexec/flua/libfreebsd/sys/linker/Makefile
@@ -0,0 +1,6 @@
+SHLIB_NAME= linker.so
+
+MAN= freebsd.sys.linker.3lua
+
+.include "Makefile.inc"
+.include <bsd.lib.mk>
diff --git a/libexec/flua/libfreebsd/sys/linker/Makefile.inc b/libexec/flua/libfreebsd/sys/linker/Makefile.inc
new file mode 100644
index 000000000000..da65c0070170
--- /dev/null
+++ b/libexec/flua/libfreebsd/sys/linker/Makefile.inc
@@ -0,0 +1,2 @@
+.PATH: ${.PARSEDIR}
+SRCS+= linker.c
diff --git a/libexec/flua/libfreebsd/sys/linker/freebsd.sys.linker.3lua b/libexec/flua/libfreebsd/sys/linker/freebsd.sys.linker.3lua
new file mode 100644
index 000000000000..34198d20463e
--- /dev/null
+++ b/libexec/flua/libfreebsd/sys/linker/freebsd.sys.linker.3lua
@@ -0,0 +1,46 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2024, Baptiste Daroussin <bapt@FreeBSD.org>
+.\"
+.Dd September 6, 2024
+.Dt FREEBSD.SYS.LINKER 3lua
+.Os
+.Sh NAME
+.Nm freebsd.sys.linker
+.Nd Lua binding to
+.Fx 's
+Linker functions
+.Sh SYNOPSIS
+.Bd -literal
+local linker = require('freebsd.sys.linker')
+.Ed
+.Pp
+.Bl -tag -width XXXX -compact
+.It Dv fileid, err, errno = linker.kldload(name)
+.It Dv ok, err, errno = linker.kldunload(fileid|name)
+.El
+.Sh DESCRIPTION
+The
+.Nm
+module is a binding to the
+.Fx 's
+linker functions.
+List of functions:
+.Bl -tag -width XXXX
+.It Dv fileid, err = freebsd.sys.linker.kldload(name)
+Load the kernel module named
+.Fa name
+and return the identifier
+.Pq fileid
+as an interger.
+.It Dv ok, err, errno = freebsd.sys.linker.kldunload(fileid|name)
+Unload the kernel module identifier either by
+.Fa name
+as a string, or
+.Fa fileid
+as an integer.
+.El
+.Sh SEE ALSO
+.Xr kldload 2 ,
+.Xr kldunload 2
diff --git a/libexec/flua/libfreebsd/sys/linker/linker.c b/libexec/flua/libfreebsd/sys/linker/linker.c
new file mode 100644
index 000000000000..c78fbb2b39d2
--- /dev/null
+++ b/libexec/flua/libfreebsd/sys/linker/linker.c
@@ -0,0 +1,86 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024, Baptiste Daroussin <bapt@FreeBSD.org>
+ */
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "bootstrap.h"
+
+int luaopen_freebsd_sys_linker(lua_State *L);
+
+static int
+lua_kldload(lua_State *L)
+{
+ const char *name;
+ int ret;
+
+ name = luaL_checkstring(L, 1);
+ ret = kldload(name);
+ if (ret == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+ lua_pushinteger(L, ret);
+ return (1);
+}
+
+static int
+lua_kldunload(lua_State *L)
+{
+ const char *name;
+ int ret, fileid;
+
+ if (lua_isinteger(L, 1)) {
+ fileid = lua_tointeger(L, 1);
+ } else {
+ name = luaL_checkstring(L, 1);
+ fileid = kldfind(name);
+ }
+ if (fileid == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+ ret = kldunload(fileid);
+ lua_pushinteger(L, ret);
+ if (ret == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+ lua_pushinteger(L, 0);
+ return (1);
+}
+
+#define REG_SIMPLE(n) { #n, lua_ ## n }
+static const struct luaL_Reg freebsd_sys_linker[] = {
+ REG_SIMPLE(kldload),
+ REG_SIMPLE(kldunload),
+ { NULL, NULL },
+};
+#undef REG_SIMPLE
+
+int
+luaopen_freebsd_sys_linker(lua_State *L)
+{
+ luaL_newlib(L, freebsd_sys_linker);
+
+ return (1);
+}
+
+FLUA_MODULE_NAMED(freebsd_sys_linker, "freebsd.sys.linker");
diff --git a/libexec/flua/libhash/Makefile b/libexec/flua/libhash/Makefile
new file mode 100644
index 000000000000..9cbd6f15acae
--- /dev/null
+++ b/libexec/flua/libhash/Makefile
@@ -0,0 +1,6 @@
+SHLIB_NAME= hash.so
+
+MAN= hash.3lua
+
+.include "Makefile.inc"
+.include <bsd.lib.mk>
diff --git a/libexec/flua/libhash/Makefile.inc b/libexec/flua/libhash/Makefile.inc
new file mode 100644
index 000000000000..d112dfe7df33
--- /dev/null
+++ b/libexec/flua/libhash/Makefile.inc
@@ -0,0 +1,3 @@
+.PATH: ${.PARSEDIR}
+SRCS+= lhash.c
+LIBADD+= md
diff --git a/libexec/flua/libhash/hash.3lua b/libexec/flua/libhash/hash.3lua
new file mode 100644
index 000000000000..1662e87f7c68
--- /dev/null
+++ b/libexec/flua/libhash/hash.3lua
@@ -0,0 +1,54 @@
+.\"
+.\" Copyright (c) 2024 Netflix, Inc.
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd February 6, 2024
+.Dt HASH 3lua
+.Os
+.Sh NAME
+.Nm new ,
+.Nm update ,
+.Nm digest ,
+.Nm hexdigest
+.Nd Lua Cryptographic hash module.
+.Sh DESCRIPTION
+The built-in cryptographic hashing Lua bindings for the are available via the
+.Ic hash
+table.
+.Ss Supported Hashing Schemes
+The following hashing schemes are supported by the hash module.
+.Bl -bullet -compact
+.It
+sha256
+.El
+.Ss APIs Supported
+.Bl -tag -width asdf -compact
+.It Fn new data
+Compute a digest based on the
+.Va data .
+.It Fn update Va data
+Using the current digest, process
+.Va data
+to compute a new digest as if all prior data had been concatenated together.
+.It Fn digest
+Return the hashed digest as a binary array.
+This resets the context.
+.It Fn hexdigest
+Take
+.Fn digest
+and convert it to an upper case hex string.
+This resets the context.
+.It Va digest_size
+Return the size of the digest, in bytes.
+.It Va block_size
+Return the block size used in bytes.
+.El
+.Sh EXAMPLES
+.Sh SEE ALSO
+.Xr sha256 3
+.Sh AUTHORS
+The
+.Nm
+man page was written by
+.An Warner Losh Aq Mt imp@FreeBSD.org .
diff --git a/libexec/flua/libhash/lhash.c b/libexec/flua/libhash/lhash.c
new file mode 100644
index 000000000000..f455f006bf27
--- /dev/null
+++ b/libexec/flua/libhash/lhash.c
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 2024 Netflix, Inc
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <lua.h>
+#include "lauxlib.h"
+#include "lhash.h"
+
+#include <sha256.h>
+#include <string.h>
+
+#include "bootstrap.h"
+
+#define SHA256_META "SHA256 meta table"
+#define SHA256_DIGEST_LEN 32
+
+/*
+ * Note C++ comments indicate the before -- after state of the stack, in with a
+ * similar convention to forth's ( ) comments. Lua indexes are from 1 and can be
+ * read left to right (leftmost is 1). Negative are relative to the end (-1 is
+ * rightmost). A '.' indicates a return value left on the stack (all values to
+ * its right). Trivial functions don't do this.
+ */
+
+/*
+ * Updates the digest with the new data passed in. Takes 1 argument, which
+ * is converted to a string.
+ */
+static int
+lua_sha256_update(lua_State *L)
+{
+ size_t len;
+ const unsigned char *data;
+ SHA256_CTX *ctx;
+
+ ctx = luaL_checkudata(L, 1, SHA256_META);
+ data = luaL_checklstring(L, 2, &len);
+ SHA256_Update(ctx, data, len);
+
+ lua_settop(L, 1);
+
+ return (1);
+}
+
+/*
+ * Finalizes the digest value and returns it as a 32-byte binary string. The ctx
+ * is zeroed.
+ */
+static int
+lua_sha256_digest(lua_State *L)
+{
+ SHA256_CTX *ctx;
+ unsigned char digest[SHA256_DIGEST_LEN];
+
+ ctx = luaL_checkudata(L, 1, SHA256_META);
+ SHA256_Final(digest, ctx);
+ lua_pushlstring(L, digest, sizeof(digest));
+
+ return (1);
+}
+
+/*
+ * Finalizes the digest value and returns it as a 64-byte ascii string of hex
+ * numbers. The ctx is zeroed.
+ */
+static int
+lua_sha256_hexdigest(lua_State *L)
+{
+ SHA256_CTX *ctx;
+ char buf[SHA256_DIGEST_LEN * 2 + 1];
+ unsigned char digest[SHA256_DIGEST_LEN];
+ static const char hex[]="0123456789abcdef";
+ int i;
+
+ ctx = luaL_checkudata(L, 1, SHA256_META);
+ SHA256_Final(digest, ctx);
+ for (i = 0; i < SHA256_DIGEST_LEN; i++) {
+ buf[i+i] = hex[digest[i] >> 4];
+ buf[i+i+1] = hex[digest[i] & 0x0f];
+ }
+ buf[i+i] = '\0';
+
+ lua_pushstring(L, buf);
+
+ return (1);
+}
+
+/*
+ * Zeros out the ctx before garbage collection. Normally this is done in
+ * obj:digest or obj:hexdigest, but if not, it will be wiped here. Lua
+ * manages freeing the ctx memory.
+ */
+static int
+lua_sha256_done(lua_State *L)
+{
+ SHA256_CTX *ctx;
+
+ ctx = luaL_checkudata(L, 1, SHA256_META);
+ memset(ctx, 0, sizeof(*ctx));
+
+ return (0);
+}
+
+/*
+ * Create object obj which accumulates the state of the sha256 digest
+ * for its contents and any subsequent obj:update call. It takes zero
+ * or 1 arguments.
+ */
+static int
+lua_sha256(lua_State *L)
+{
+ SHA256_CTX *ctx;
+ int top;
+
+ /* We take 0 or 1 args */
+ top = lua_gettop(L); // data -- data
+ if (top > 1) {
+ lua_pushnil(L);
+ return (1);
+ }
+
+ ctx = lua_newuserdata(L, sizeof(*ctx)); // data -- data ctx
+ SHA256_Init(ctx);
+ if (top == 1) {
+ size_t len;
+ const unsigned char *data;
+
+ data = luaL_checklstring(L, 1, &len);
+ SHA256_Update(ctx, data, len);
+ }
+ luaL_setmetatable(L, SHA256_META); // data ctx -- data ctx
+
+ return (1); // data . ctx
+}
+
+/*
+ * Setup the metatable to manage our userdata that we create in lua_sha256. We
+ * request a finalization call with __gc so we can zero out the ctx buffer so
+ * that we don't leak secrets if obj:digest or obj:hexdigest aren't called.
+ */
+static void
+register_metatable_sha256(lua_State *L)
+{
+ luaL_newmetatable(L, SHA256_META); // -- meta
+
+ lua_newtable(L); // meta -- meta tbl
+ lua_pushcfunction(L, lua_sha256_update); // meta tbl -- meta tbl fn
+ lua_setfield(L, -2, "update"); // meta tbl fn -- meta tbl
+ lua_pushcfunction(L, lua_sha256_digest); // meta tbl -- meta tbl fn
+ lua_setfield(L, -2, "digest"); // meta tbl fn -- meta tbl
+ lua_pushcfunction(L, lua_sha256_hexdigest); // meta tbl -- meta tbl fn
+ lua_setfield(L, -2, "hexdigest"); // meta tbl fn -- meta tbl
+
+ /* Associate tbl with metatable */
+ lua_setfield(L, -2, "__index"); // meta tbl -- meta
+ lua_pushcfunction(L, lua_sha256_done); // meta -- meta fn
+ lua_setfield(L, -2, "__gc"); // meta fn -- meta
+
+ lua_pop(L, 1); // meta --
+}
+
+#define REG_SIMPLE(n) { #n, lua_ ## n }
+static const struct luaL_Reg hashlib[] = {
+ REG_SIMPLE(sha256),
+ { NULL, NULL },
+};
+#undef REG_SIMPLE
+
+int
+luaopen_hash(lua_State *L)
+{
+ register_metatable_sha256(L);
+
+ luaL_newlib(L, hashlib);
+
+ return 1;
+}
+
+#ifndef _STANDALONE
+FLUA_MODULE(hash);
+#endif
diff --git a/libexec/flua/libhash/lhash.h b/libexec/flua/libhash/lhash.h
new file mode 100644
index 000000000000..c1e9788a55a3
--- /dev/null
+++ b/libexec/flua/libhash/lhash.h
@@ -0,0 +1,11 @@
+/*-
+ * Copyright (c) 2024 Netflix, Inc
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <lua.h>
+
+int luaopen_hash(lua_State *L);
diff --git a/libexec/flua/libjail/Makefile b/libexec/flua/libjail/Makefile
new file mode 100644
index 000000000000..b9c8bdc39095
--- /dev/null
+++ b/libexec/flua/libjail/Makefile
@@ -0,0 +1,6 @@
+SHLIB_NAME= jail.so
+
+MAN= jail.3lua
+
+.include "Makefile.inc"
+.include <bsd.lib.mk>
diff --git a/libexec/flua/libjail/Makefile.inc b/libexec/flua/libjail/Makefile.inc
new file mode 100644
index 000000000000..a896bf38c65b
--- /dev/null
+++ b/libexec/flua/libjail/Makefile.inc
@@ -0,0 +1,3 @@
+.PATH: ${.PARSEDIR}
+SRCS+= lua_jail.c
+LIBADD+= jail
diff --git a/libexec/flua/libjail/jail.3lua b/libexec/flua/libjail/jail.3lua
new file mode 100644
index 000000000000..59cbd2dc228c
--- /dev/null
+++ b/libexec/flua/libjail/jail.3lua
@@ -0,0 +1,277 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2020, Ryan Moeller <freqlabs@FreeBSD.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd October 24, 2020
+.Dt JAIL 3lua
+.Os
+.Sh NAME
+.Nm attach ,
+.Nm getid ,
+.Nm getname ,
+.Nm list ,
+.Nm allparams ,
+.Nm getparams ,
+.Nm remove ,
+.Nm setparams ,
+.Nm CREATE ,
+.Nm UPDATE ,
+.Nm ATTACH ,
+.Nm DYING
+.Nd Lua binding to
+.Xr jail 3
+.Sh SYNOPSIS
+.Bd -literal
+local jail = require('jail')
+.Ed
+.Pp
+.Bl -tag -width XXXX -compact
+.It Dv ok, err = jail.attach(jid|name)
+.It Dv jid, err = jail.getid(name)
+.It Dv name, err = jail.getname(jid)
+.It Dv params, err = jail.allparams()
+.It Dv iter, jail_obj = jail.list([params])
+.It Dv jid, res = jail.getparams(jid|name, params [, flags ] )
+.It Dv ok, err = jail.remove(jid|name)
+.It Dv jid, err = jail.setparams(jid|name, params, flags )
+.It Dv jail.CREATE
+.It Dv jail.UPDATE
+.It Dv jail.ATTACH
+.It Dv jail.DYING
+.El
+.Sh DESCRIPTION
+The
+.Nm jail
+module is a binding to the
+.Xr jail 3
+library.
+It provides a string-oriented interface for the
+.Xr jail_get 2
+and
+.Xr jail_set 2
+system calls.
+.Bl -tag -width XXXX
+.It Dv ok, err = jail.attach(jid|name)
+Attach to the given jail, identified by an integer
+.Fa jid
+or the
+.Fa name .
+.It Dv jid, err = jail.getid(name)
+Get the jail identifier
+.Pq jid
+as an integer.
+.Fa name
+is the name of a jail or a jid in the form of a string.
+.It Dv name, err = jail.getname(jid)
+Get the name of a jail as a string for the given
+.Fa jid
+.Pq an integer .
+.It Dv iter, jail_obj = jail.list([params])
+Returns an iterator over running jails on the system.
+.Dv params
+is a list of parameters to fetch for each jail as we iterate.
+.Dv jid
+and
+.Dv name
+will always be returned, and may be omitted from
+.Dv params .
+Additionally,
+.Dv params
+may be omitted or an empty table, but not nil.
+.Pp
+See
+.Sx EXAMPLES .
+.It Dv params, err = jail.allparams()
+Get a list of all supported parameter names
+.Pq as strings .
+See
+.Xr jail 8
+for descriptions of the core jail parameters.
+.It Dv jid, res = jail.getparams(jid|name, params [, flags ] )
+Get a table of the requested parameters for the given jail.
+.Nm jid|name
+can either be the jid as an integer or the jid or name as a string.
+.Nm params
+is a list of parameter names.
+.Nm flags
+is an optional integer representing the flag bits to apply for the operation.
+See the list of flags below.
+Only the
+.Dv DYING
+flag is valid to set.
+.It Dv ok, err = jail.remove(jid|name)
+Remove the given jail, identified by an integer
+.Fa jid
+or the
+.Fa name .
+.It Dv jid, err = jail.setparams(jid|name, params [, flags ] )
+Set parameters for a given jail.
+This is used to create, update, attach to, or destroy a jail.
+.Nm jid|name
+can either be the jid as an integer or the jid or name as a string.
+.Nm params
+is a table of parameters to apply to the jail, where each key in the table
+is a parameter name as a string and each value is a string that will be
+converted to the internal value type by
+.Xr jailparam_import 3 .
+.Nm flags
+is an optional integer representing the flag bits to apply for the operation.
+See the list of flags below.
+.El
+.Pp
+The
+.Nm flags
+arguments are an integer bitwise-or combination of one or more of the following
+flags:
+.Bl -tag -width XXXX
+.It Dv jail.CREATE
+Used with
+.Fn setparams
+to create a new jail.
+The jail must not already exist, unless combined with
+.Dv UPDATE .
+.It Dv jail.UPDATE
+Used with
+.Fn setparams
+to modify an existing jail.
+The jail must already exist, unless combined with
+.Dv CREATE .
+.It Dv jail.ATTACH
+Used with
+.Fn setparams
+in combination with
+.Dv CREATE
+or
+.Dv UPDATE
+to attach the current process to a jail.
+.It Dv jail.DYING
+Allow operating on a jail that is in the process of being removed.
+.El
+.Sh RETURN VALUES
+The
+.Fn getid
+and
+.Fn setparams
+functions return a jail identifier integer on success, or
+.Dv nil
+and an error message string if an error occurred.
+.Pp
+The
+.Fn getname
+function returns a jail name string on success, or
+.Dv nil
+and an error message string if an error occurred.
+.Pp
+The
+.Fn allparams
+function returns a list of parameter name strings on success, or
+.Dv nil
+and an error message string if an error occurred.
+.Pp
+The
+.Fn getparams
+function returns a jail identifier integer and a table of jail parameters
+with parameter name strings as keys and strings for values on success, or
+.Dv nil
+and an error message string if an error occurred.
+.Pp
+The
+.Fn list
+function returns an iterator over the list of running jails.
+.Pp
+The
+.Fn attach
+and
+.Fn remove
+functions return true on success, or
+.Dv nil
+and an error message string if an error occurred.
+.Sh EXAMPLES
+Set the hostname of jail
+.Dq foo
+to
+.Dq foo.bar :
+.Bd -literal -offset indent
+local jail = require('jail')
+
+jid, err = jail.setparams("foo", {["host.hostname"]="foo.bar"},
+ jail.UPDATE)
+if not jid then
+ error(err)
+end
+.Ed
+.Pp
+Retrieve the hostname of jail
+.Dq foo :
+.Bd -literal -offset indent
+local jail = require('jail')
+
+jid, res = jail.getparams("foo", {"host.hostname"})
+if not jid then
+ error(res)
+end
+print(res["host.hostname"])
+.Ed
+.Pp
+Iterate over jails on the system:
+.Bd -literal -offset indent
+local jail = require('jail')
+
+-- Recommended: just loop over it
+for jparams in jail.list() do
+ print(jparams["jid"] .. " = " .. jparams["name"])
+end
+
+-- Request path and hostname, too
+for jparams in jail.list({"path", "host.hostname"}) do
+ print(jparams["host.hostname"] .. " mounted at " .. jparams["path"])
+end
+
+-- Raw iteration protocol
+local iter, jail_obj = jail.list()
+
+-- Request the first params
+local jparams = jail_obj:next()
+while jparams do
+ print(jparams["jid"] .. " = " .. jparams["name"])
+ -- Subsequent calls may return nil
+ jparams = jail_obj:next()
+end
+.Ed
+.Sh SEE ALSO
+.Xr jail 2 ,
+.Xr jail 3 ,
+.Xr jail 8
+.Sh HISTORY
+The
+.Nm jail
+Lua module for flua first appeared in
+.Fx 13.0 .
+.Sh AUTHORS
+.An Ryan Moeller ,
+with inspiration from
+.Nx
+gpio(3lua), by
+.An Mark Balmer .
diff --git a/libexec/flua/libjail/lua_jail.c b/libexec/flua/libjail/lua_jail.c
new file mode 100644
index 000000000000..8c3ec6c1d500
--- /dev/null
+++ b/libexec/flua/libjail/lua_jail.c
@@ -0,0 +1,722 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020, Ryan Moeller <freqlabs@FreeBSD.org>
+ * Copyright (c) 2020, Kyle Evans <kevans@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <errno.h>
+#include <jail.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "bootstrap.h"
+
+#define JAIL_METATABLE "jail iterator metatable"
+
+/*
+ * Taken from RhodiumToad's lspawn implementation, let static analyzers make
+ * better decisions about the behavior after we raise an error.
+ */
+#if defined(LUA_VERSION_NUM) && defined(LUA_API)
+LUA_API int (lua_error) (lua_State *L) __dead2;
+#endif
+#if defined(LUA_ERRFILE) && defined(LUALIB_API)
+LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg) __dead2;
+LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname) __dead2;
+LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...) __dead2;
+#endif
+
+int luaopen_jail(lua_State *);
+
+typedef bool (*getparam_filter)(const char *, void *);
+
+static void getparam_table(lua_State *L, int paramindex,
+ struct jailparam *params, size_t paramoff, size_t *params_countp,
+ getparam_filter keyfilt, void *udata);
+
+struct l_jail_iter {
+ struct jailparam *params;
+ size_t params_count;
+ int jid;
+};
+
+static bool
+l_jail_filter(const char *param_name, void *data __unused)
+{
+
+ /*
+ * Allowing lastjid will mess up our iteration over all jails on the
+ * system, as this is a special parameter that indicates where the search
+ * starts from. We'll always add jid and name, so just silently remove
+ * these.
+ */
+ return (strcmp(param_name, "lastjid") != 0 &&
+ strcmp(param_name, "jid") != 0 &&
+ strcmp(param_name, "name") != 0);
+}
+
+static int
+l_jail_iter_next(lua_State *L)
+{
+ struct l_jail_iter *iter, **iterp;
+ struct jailparam *jp;
+ int serrno;
+
+ iterp = (struct l_jail_iter **)luaL_checkudata(L, 1, JAIL_METATABLE);
+ iter = *iterp;
+ luaL_argcheck(L, iter != NULL, 1, "closed jail iterator");
+
+ jp = iter->params;
+ /* Populate lastjid; we must keep it in params[0] for our sake. */
+ if (jailparam_import_raw(&jp[0], &iter->jid, sizeof(iter->jid))) {
+ jailparam_free(jp, iter->params_count);
+ free(jp);
+ free(iter);
+ *iterp = NULL;
+ return (luaL_error(L, "jailparam_import_raw: %s", jail_errmsg));
+ }
+
+ /* The list of requested params was populated back in l_list(). */
+ iter->jid = jailparam_get(jp, iter->params_count, 0);
+ if (iter->jid == -1) {
+ /*
+ * We probably got an ENOENT to signify the end of the jail
+ * listing, but just in case we didn't; stash it off and start
+ * cleaning up. We'll handle non-ENOENT errors later.
+ */
+ serrno = errno;
+ jailparam_free(jp, iter->params_count);
+ free(iter->params);
+ free(iter);
+ *iterp = NULL;
+ if (serrno != ENOENT)
+ return (luaL_error(L, "jailparam_get: %s",
+ strerror(serrno)));
+ return (0);
+ }
+
+ /*
+ * Finally, we'll fill in the return table with whatever parameters the
+ * user requested, in addition to the ones we forced with exception to
+ * lastjid.
+ */
+ lua_newtable(L);
+ for (size_t i = 0; i < iter->params_count; ++i) {
+ char *value;
+
+ jp = &iter->params[i];
+ if (strcmp(jp->jp_name, "lastjid") == 0)
+ continue;
+ value = jailparam_export(jp);
+ lua_pushstring(L, value);
+ lua_setfield(L, -2, jp->jp_name);
+ free(value);
+ }
+
+ return (1);
+}
+
+static int
+l_jail_iter_close(lua_State *L)
+{
+ struct l_jail_iter *iter, **iterp;
+
+ /*
+ * Since we're using this as the __gc method as well, there's a good
+ * chance that it's already been cleaned up by iterating to the end of
+ * the list.
+ */
+ iterp = (struct l_jail_iter **)lua_touserdata(L, 1);
+ iter = *iterp;
+ if (iter == NULL)
+ return (0);
+
+ jailparam_free(iter->params, iter->params_count);
+ free(iter->params);
+ free(iter);
+ *iterp = NULL;
+ return (0);
+}
+
+static int
+l_list(lua_State *L)
+{
+ struct l_jail_iter *iter;
+ int nargs;
+
+ nargs = lua_gettop(L);
+ if (nargs >= 1)
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ iter = malloc(sizeof(*iter));
+ if (iter == NULL)
+ return (luaL_error(L, "malloc: %s", strerror(errno)));
+
+ /*
+ * lastjid, jid, name + length of the table. This may be too much if
+ * we have duplicated one of those fixed parameters.
+ */
+ iter->params_count = 3 + (nargs != 0 ? lua_rawlen(L, 1) : 0);
+ iter->params = malloc(iter->params_count * sizeof(*iter->params));
+ if (iter->params == NULL) {
+ free(iter);
+ return (luaL_error(L, "malloc params: %s", strerror(errno)));
+ }
+
+ /* The :next() method will populate lastjid before jail_getparam(). */
+ if (jailparam_init(&iter->params[0], "lastjid") == -1) {
+ free(iter->params);
+ free(iter);
+ return (luaL_error(L, "jailparam_init: %s", jail_errmsg));
+ }
+ /* These two will get populated by jail_getparam(). */
+ if (jailparam_init(&iter->params[1], "jid") == -1) {
+ jailparam_free(iter->params, 1);
+ free(iter->params);
+ free(iter);
+ return (luaL_error(L, "jailparam_init: %s",
+ jail_errmsg));
+ }
+ if (jailparam_init(&iter->params[2], "name") == -1) {
+ jailparam_free(iter->params, 2);
+ free(iter->params);
+ free(iter);
+ return (luaL_error(L, "jailparam_init: %s",
+ jail_errmsg));
+ }
+
+ /*
+ * We only need to process additional arguments if we were given any.
+ * That is, we don't descend into getparam_table if we're passed nothing
+ * or an empty table.
+ */
+ iter->jid = 0;
+ if (iter->params_count != 3)
+ getparam_table(L, 1, iter->params, 2, &iter->params_count,
+ l_jail_filter, NULL);
+
+ /*
+ * Part of the iterator magic. We give it an iterator function with a
+ * metatable defining next() and close() that can be used for manual
+ * iteration. iter->jid is how we track which jail we last iterated, to
+ * be supplied as "lastjid".
+ */
+ lua_pushcfunction(L, l_jail_iter_next);
+ *(struct l_jail_iter **)lua_newuserdata(L,
+ sizeof(struct l_jail_iter **)) = iter;
+ luaL_getmetatable(L, JAIL_METATABLE);
+ lua_setmetatable(L, -2);
+ return (2);
+}
+
+static void
+register_jail_metatable(lua_State *L)
+{
+ luaL_newmetatable(L, JAIL_METATABLE);
+ lua_newtable(L);
+ lua_pushcfunction(L, l_jail_iter_next);
+ lua_setfield(L, -2, "next");
+ lua_pushcfunction(L, l_jail_iter_close);
+ lua_setfield(L, -2, "close");
+
+ lua_setfield(L, -2, "__index");
+
+ lua_pushcfunction(L, l_jail_iter_close);
+ lua_setfield(L, -2, "__gc");
+
+ lua_pop(L, 1);
+}
+
+static int
+l_getid(lua_State *L)
+{
+ const char *name;
+ int jid;
+
+ name = luaL_checkstring(L, 1);
+ jid = jail_getid(name);
+ if (jid == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, jail_errmsg);
+ return (2);
+ }
+ lua_pushinteger(L, jid);
+ return (1);
+}
+
+static int
+l_getname(lua_State *L)
+{
+ char *name;
+ int jid;
+
+ jid = luaL_checkinteger(L, 1);
+ name = jail_getname(jid);
+ if (name == NULL) {
+ lua_pushnil(L);
+ lua_pushstring(L, jail_errmsg);
+ return (2);
+ }
+ lua_pushstring(L, name);
+ free(name);
+ return (1);
+}
+
+static int
+l_allparams(lua_State *L)
+{
+ struct jailparam *params;
+ int params_count;
+
+ params_count = jailparam_all(&params);
+ if (params_count == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, jail_errmsg);
+ return (2);
+ }
+ lua_newtable(L);
+ for (int i = 0; i < params_count; ++i) {
+ lua_pushstring(L, params[i].jp_name);
+ lua_rawseti(L, -2, i + 1);
+ }
+ jailparam_free(params, params_count);
+ free(params);
+ return (1);
+}
+
+static void
+getparam_table(lua_State *L, int paramindex, struct jailparam *params,
+ size_t params_off, size_t *params_countp, getparam_filter keyfilt,
+ void *udata)
+{
+ size_t params_count;
+ int skipped;
+
+ params_count = *params_countp;
+ skipped = 0;
+ for (size_t i = 1 + params_off; i < params_count; ++i) {
+ const char *param_name;
+
+ lua_rawgeti(L, -1, i - params_off);
+ param_name = lua_tostring(L, -1);
+ if (param_name == NULL) {
+ jailparam_free(params, i - skipped);
+ free(params);
+ luaL_argerror(L, paramindex,
+ "param names must be strings");
+ }
+ lua_pop(L, 1);
+ if (keyfilt != NULL && !keyfilt(param_name, udata)) {
+ ++skipped;
+ continue;
+ }
+ if (jailparam_init(&params[i - skipped], param_name) == -1) {
+ jailparam_free(params, i - skipped);
+ free(params);
+ luaL_error(L, "jailparam_init: %s", jail_errmsg);
+ }
+ }
+ *params_countp -= skipped;
+}
+
+struct getparams_filter_args {
+ int filter_type;
+};
+
+static bool
+l_getparams_filter(const char *param_name, void *udata)
+{
+ struct getparams_filter_args *gpa;
+
+ gpa = udata;
+
+ /* Skip name or jid, whichever was given. */
+ if (gpa->filter_type == LUA_TSTRING) {
+ if (strcmp(param_name, "name") == 0)
+ return (false);
+ } else /* type == LUA_TNUMBER */ {
+ if (strcmp(param_name, "jid") == 0)
+ return (false);
+ }
+
+ return (true);
+}
+
+static int
+l_getparams(lua_State *L)
+{
+ const char *name;
+ struct jailparam *params;
+ size_t params_count;
+ struct getparams_filter_args gpa;
+ int flags, jid, type;
+
+ type = lua_type(L, 1);
+ luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
+ "expected a jail name (string) or id (integer)");
+ luaL_checktype(L, 2, LUA_TTABLE);
+ params_count = 1 + lua_rawlen(L, 2);
+ flags = luaL_optinteger(L, 3, 0);
+
+ params = malloc(params_count * sizeof(struct jailparam));
+ if (params == NULL)
+ return (luaL_error(L, "malloc: %s", strerror(errno)));
+
+ /*
+ * Set the jail name or id param as determined by the first arg.
+ */
+
+ if (type == LUA_TSTRING) {
+ if (jailparam_init(&params[0], "name") == -1) {
+ free(params);
+ return (luaL_error(L, "jailparam_init: %s",
+ jail_errmsg));
+ }
+ name = lua_tostring(L, 1);
+ if (jailparam_import(&params[0], name) == -1) {
+ jailparam_free(params, 1);
+ free(params);
+ return (luaL_error(L, "jailparam_import: %s",
+ jail_errmsg));
+ }
+ } else /* type == LUA_TNUMBER */ {
+ if (jailparam_init(&params[0], "jid") == -1) {
+ free(params);
+ return (luaL_error(L, "jailparam_init: %s",
+ jail_errmsg));
+ }
+ jid = lua_tointeger(L, 1);
+ if (jailparam_import_raw(&params[0], &jid, sizeof(jid)) == -1) {
+ jailparam_free(params, 1);
+ free(params);
+ return (luaL_error(L, "jailparam_import_raw: %s",
+ jail_errmsg));
+ }
+ }
+
+ /*
+ * Set the remaining param names being requested.
+ */
+ gpa.filter_type = type;
+ getparam_table(L, 2, params, 0, &params_count, l_getparams_filter, &gpa);
+
+ /*
+ * Get the values and convert to a table.
+ */
+
+ jid = jailparam_get(params, params_count, flags);
+ if (jid == -1) {
+ jailparam_free(params, params_count);
+ free(params);
+ lua_pushnil(L);
+ lua_pushstring(L, jail_errmsg);
+ return (2);
+ }
+ lua_pushinteger(L, jid);
+
+ lua_newtable(L);
+ for (size_t i = 0; i < params_count; ++i) {
+ char *value;
+
+ if (params[i].jp_flags & JP_KEYVALUE &&
+ params[i].jp_valuelen == 0) {
+ /* Communicate back a missing key. */
+ lua_pushnil(L);
+ } else {
+ value = jailparam_export(&params[i]);
+ lua_pushstring(L, value);
+ free(value);
+ }
+
+ lua_setfield(L, -2, params[i].jp_name);
+ }
+
+ jailparam_free(params, params_count);
+ free(params);
+
+ return (2);
+}
+
+static int
+l_setparams(lua_State *L)
+{
+ const char *name;
+ struct jailparam *params;
+ size_t params_count;
+ int flags, jid, type;
+
+ type = lua_type(L, 1);
+ luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
+ "expected a jail name (string) or id (integer)");
+ luaL_checktype(L, 2, LUA_TTABLE);
+
+ lua_pushnil(L);
+ for (params_count = 1; lua_next(L, 2) != 0; ++params_count)
+ lua_pop(L, 1);
+
+ flags = luaL_optinteger(L, 3, 0);
+
+ params = malloc(params_count * sizeof(struct jailparam));
+ if (params == NULL)
+ return (luaL_error(L, "malloc: %s", strerror(errno)));
+
+ /*
+ * Set the jail name or id param as determined by the first arg.
+ */
+
+ if (type == LUA_TSTRING) {
+ if (jailparam_init(&params[0], "name") == -1) {
+ free(params);
+ return (luaL_error(L, "jailparam_init: %s",
+ jail_errmsg));
+ }
+ name = lua_tostring(L, 1);
+ if (jailparam_import(&params[0], name) == -1) {
+ jailparam_free(params, 1);
+ free(params);
+ return (luaL_error(L, "jailparam_import: %s",
+ jail_errmsg));
+ }
+ } else /* type == LUA_TNUMBER */ {
+ if (jailparam_init(&params[0], "jid") == -1) {
+ free(params);
+ return (luaL_error(L, "jailparam_init: %s",
+ jail_errmsg));
+ }
+ jid = lua_tointeger(L, 1);
+ if (jailparam_import_raw(&params[0], &jid, sizeof(jid)) == -1) {
+ jailparam_free(params, 1);
+ free(params);
+ return (luaL_error(L, "jailparam_import_raw: %s",
+ jail_errmsg));
+ }
+ }
+
+ /*
+ * Set the rest of the provided params.
+ */
+
+ lua_pushnil(L);
+ for (size_t i = 1; i < params_count && lua_next(L, 2) != 0; ++i) {
+ const char *value;
+
+ name = lua_tostring(L, -2);
+ if (name == NULL) {
+ jailparam_free(params, i);
+ free(params);
+ return (luaL_argerror(L, 2,
+ "param names must be strings"));
+ }
+ if (jailparam_init(&params[i], name) == -1) {
+ jailparam_free(params, i);
+ free(params);
+ return (luaL_error(L, "jailparam_init: %s",
+ jail_errmsg));
+ }
+
+ value = lua_tostring(L, -1);
+ /* Allow passing NULL for key removal. */
+ if (value == NULL && !(params[i].jp_flags & JP_KEYVALUE)) {
+ jailparam_free(params, i + 1);
+ free(params);
+ return (luaL_argerror(L, 2,
+ "param values must be strings"));
+ }
+ if (jailparam_import(&params[i], value) == -1) {
+ jailparam_free(params, i + 1);
+ free(params);
+ return (luaL_error(L, "jailparam_import: %s",
+ jail_errmsg));
+ }
+
+ lua_pop(L, 1);
+ }
+
+ /*
+ * Attempt to set the params.
+ */
+
+ jid = jailparam_set(params, params_count, flags);
+ if (jid == -1) {
+ jailparam_free(params, params_count);
+ free(params);
+ lua_pushnil(L);
+ lua_pushstring(L, jail_errmsg);
+ return (2);
+ }
+ lua_pushinteger(L, jid);
+
+ jailparam_free(params, params_count);
+ free(params);
+ return (1);
+}
+
+static int
+l_attach(lua_State *L)
+{
+ int jid, type;
+
+ type = lua_type(L, 1);
+ luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
+ "expected a jail name (string) or id (integer)");
+
+ if (lua_isstring(L, 1)) {
+ /* Resolve it to a jid. */
+ jid = jail_getid(lua_tostring(L, 1));
+ if (jid == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, jail_errmsg);
+ return (2);
+ }
+ } else {
+ jid = lua_tointeger(L, 1);
+ }
+
+ if (jail_attach(jid) == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ return (2);
+ }
+
+ lua_pushboolean(L, 1);
+ return (1);
+}
+
+static int
+l_remove(lua_State *L)
+{
+ int jid, type;
+
+ type = lua_type(L, 1);
+ luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
+ "expected a jail name (string) or id (integer)");
+
+ if (lua_isstring(L, 1)) {
+ /* Resolve it to a jid. */
+ jid = jail_getid(lua_tostring(L, 1));
+ if (jid == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, jail_errmsg);
+ return (2);
+ }
+ } else {
+ jid = lua_tointeger(L, 1);
+ }
+
+ if (jail_remove(jid) == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ return (2);
+ }
+
+ lua_pushboolean(L, 1);
+ return (1);
+}
+
+static const struct luaL_Reg l_jail[] = {
+ /** Get id of a jail by name.
+ * @param name jail name (string)
+ * @return jail id (integer)
+ * or nil, error (string) on error
+ */
+ {"getid", l_getid},
+ /** Get name of a jail by id.
+ * @param jid jail id (integer)
+ * @return jail name (string)
+ * or nil, error (string) on error
+ */
+ {"getname", l_getname},
+ /** Get a list of all known jail parameters.
+ * @return list of jail parameter names (table of strings)
+ * or nil, error (string) on error
+ */
+ {"allparams", l_allparams},
+ /** Get the listed params for a given jail.
+ * @param jail jail name (string) or id (integer)
+ * @param params list of parameter names (table of strings)
+ * @param flags optional flags (integer)
+ * @return jid (integer), params (table of [string] = string)
+ * or nil, error (string) on error
+ */
+ {"getparams", l_getparams},
+ /** Set params for a given jail.
+ * @param jail jail name (string) or id (integer)
+ * @param params params and values (table of [string] = string)
+ * @param flags optional flags (integer)
+ * @return jid (integer)
+ * or nil, error (string) on error
+ */
+ {"setparams", l_setparams},
+ /** Get a list of jail parameters for running jails on the system.
+ * @param params optional list of parameter names (table of
+ * strings)
+ * @return iterator (function), jail_obj (object) with next and
+ * close methods
+ */
+ {"list", l_list},
+ /** Attach to a running jail.
+ * @param jail jail name (string) or id (integer)
+ * @return true (boolean)
+ * or nil, error (string) on error
+ */
+ {"attach", l_attach},
+ /** Remove a running jail.
+ * @param jail jail name (string) or id (integer)
+ * @return true (boolean)
+ * or nil, error (string) on error
+ */
+ {"remove", l_remove},
+ {NULL, NULL}
+};
+
+int
+luaopen_jail(lua_State *L)
+{
+ lua_newtable(L);
+
+ luaL_setfuncs(L, l_jail, 0);
+
+ lua_pushinteger(L, JAIL_CREATE);
+ lua_setfield(L, -2, "CREATE");
+ lua_pushinteger(L, JAIL_UPDATE);
+ lua_setfield(L, -2, "UPDATE");
+ lua_pushinteger(L, JAIL_ATTACH);
+ lua_setfield(L, -2, "ATTACH");
+ lua_pushinteger(L, JAIL_DYING);
+ lua_setfield(L, -2, "DYING");
+
+ register_jail_metatable(L);
+
+ return (1);
+}
+
+FLUA_MODULE(jail);
diff --git a/libexec/flua/liblyaml/Makefile b/libexec/flua/liblyaml/Makefile
new file mode 100644
index 000000000000..8d1432acd325
--- /dev/null
+++ b/libexec/flua/liblyaml/Makefile
@@ -0,0 +1,4 @@
+SHLIB_NAME= yaml.so
+
+.include "Makefile.inc"
+.include <bsd.lib.mk>
diff --git a/libexec/flua/liblyaml/Makefile.inc b/libexec/flua/liblyaml/Makefile.inc
new file mode 100644
index 000000000000..caa1f37b57eb
--- /dev/null
+++ b/libexec/flua/liblyaml/Makefile.inc
@@ -0,0 +1,20 @@
+WARNS= 1
+
+LYAMLSRC?= ${SRCTOP}/contrib/lyaml
+.PATH: ${LYAMLSRC}/ext/yaml ${LYAMLSRC}/lib/lyaml
+SRCS+= emitter.c \
+ parser.c \
+ scanner.c \
+ yaml.c
+CFLAGS+= \
+ -I${LYAMLSRC}/ext/yaml \
+ -I${SRCTOP}/contrib/libyaml/include \
+ -DVERSION=\"6.2.8\"
+LIBADD+= yaml
+
+FILESGROUPS+= YAML
+YAML= explicit.lua \
+ functional.lua \
+ implicit.lua \
+ init.lua
+YAMLDIR= ${SHAREDIR}/flua/lyaml
diff --git a/libexec/flua/libucl/Makefile b/libexec/flua/libucl/Makefile
new file mode 100644
index 000000000000..32d76d1ea1ad
--- /dev/null
+++ b/libexec/flua/libucl/Makefile
@@ -0,0 +1,4 @@
+SHLIB_NAME= ucl.so
+
+.include "Makefile.inc"
+.include <bsd.lib.mk>
diff --git a/libexec/flua/libucl/Makefile.inc b/libexec/flua/libucl/Makefile.inc
new file mode 100644
index 000000000000..70fb0f265635
--- /dev/null
+++ b/libexec/flua/libucl/Makefile.inc
@@ -0,0 +1,12 @@
+.if ${WARNS:U6} > 2
+WARNS= 2
+.endif
+
+UCLSRC?= ${SRCTOP}/contrib/libucl
+.PATH: ${UCLSRC}/lua
+SRCS+= lua_ucl.c
+CFLAGS+= \
+ -I${UCLSRC}/include \
+ -I${UCLSRC}/src \
+ -I${UCLSRC}/uthash
+LIBADD+= ucl
diff --git a/libexec/flua/linit_flua.c b/libexec/flua/linit_flua.c
new file mode 100644
index 000000000000..65356c938671
--- /dev/null
+++ b/libexec/flua/linit_flua.c
@@ -0,0 +1,95 @@
+/*
+** $Id: linit.c,v 1.39.1.1 2017/04/19 17:20:42 roberto Exp $
+** Initialization of libraries for lua.c and other clients
+** See Copyright Notice in lua.h
+*/
+
+
+#define linit_c
+#define LUA_LIB
+
+/*
+** If you embed Lua in your program and need to open the standard
+** libraries, call luaL_openlibs in your program. If you need a
+** different set of libraries, copy this file to your project and edit
+** it to suit your needs.
+**
+** You can also *preload* libraries, so that a later 'require' can
+** open the library, which is already linked to the application.
+** For that, do the following code:
+**
+** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
+** lua_pushcfunction(L, luaopen_modname);
+** lua_setfield(L, -2, modname);
+** lua_pop(L, 1); // remove PRELOAD table
+*/
+
+#include "lprefix.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "lualib.h"
+#include "lauxlib.h"
+#include "lposix.h"
+
+#include "bootstrap.h"
+
+/*
+** these libs are loaded by lua.c and are readily available to any Lua
+** program
+*/
+static const luaL_Reg loadedlibs[] = {
+ {"_G", luaopen_base},
+ {LUA_LOADLIBNAME, luaopen_package},
+ {LUA_COLIBNAME, luaopen_coroutine},
+ {LUA_TABLIBNAME, luaopen_table},
+ {LUA_IOLIBNAME, luaopen_io},
+ {LUA_OSLIBNAME, luaopen_os},
+ {LUA_STRLIBNAME, luaopen_string},
+ {LUA_MATHLIBNAME, luaopen_math},
+ {LUA_UTF8LIBNAME, luaopen_utf8},
+ {LUA_DBLIBNAME, luaopen_debug},
+#if defined(LUA_COMPAT_BITLIB)
+ {LUA_BITLIBNAME, luaopen_bit32},
+#endif
+ /* FreeBSD Extensions */
+ {"posix", luaopen_posix},
+ {NULL, NULL}
+};
+
+#ifdef BOOTSTRAPPING
+static void __attribute__((constructor)) flua_init_env(void) {
+ /*
+ * This happens in the middle of luaopen_package(). We could move it into
+ * flua_setup_mods(), but it seems better to avoid its timing being so
+ * important that it would break some of our bootstrap modules if someone
+ * were to reorder things.
+ */
+ if (getenv("LUA_PATH") == NULL)
+ setenv("LUA_PATH", BOOTSTRAP_FLUA_PATH, 1);
+}
+
+static void flua_setup_mods (lua_State *L) {
+ const luaL_Reg **flib;
+
+ SET_FOREACH(flib, FLUA_MODULE_SETNAME) {
+ luaL_requiref(L, (*flib)->name, (*flib)->func, 1);
+ lua_pop(L, 1); /* remove lib */
+ }
+};
+#endif
+
+LUALIB_API void luaL_openlibs (lua_State *L) {
+ const luaL_Reg *lib;
+ /* "require" functions from 'loadedlibs' and set results to global table */
+ for (lib = loadedlibs; lib->func; lib++) {
+ luaL_requiref(L, lib->name, lib->func, 1);
+ lua_pop(L, 1); /* remove lib */
+ }
+#ifdef BOOTSTRAPPING
+ flua_setup_mods(L);
+#endif
+}
diff --git a/libexec/flua/modules/lposix.c b/libexec/flua/modules/lposix.c
new file mode 100644
index 000000000000..75cdd345aeaa
--- /dev/null
+++ b/libexec/flua/modules/lposix.c
@@ -0,0 +1,699 @@
+/*-
+ * Copyright (c) 2019, 2023 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <grp.h>
+#include <libgen.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <lua.h>
+#include "lauxlib.h"
+#include "lposix.h"
+
+static void
+enforce_max_args(lua_State *L, int max)
+{
+ int narg;
+
+ narg = lua_gettop(L);
+ luaL_argcheck(L, narg <= max, max + 1, "too many arguments");
+}
+
+/*
+ * Minimal implementation of luaposix needed for internal FreeBSD bits.
+ */
+static int
+lua__exit(lua_State *L)
+{
+ int code;
+
+ enforce_max_args(L, 1);
+ code = luaL_checkinteger(L, 1);
+
+ _exit(code);
+}
+
+static int
+lua_basename(lua_State *L)
+{
+ char *inpath, *outpath;
+
+ enforce_max_args(L, 1);
+ inpath = strdup(luaL_checkstring(L, 1));
+ if (inpath == NULL) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(ENOMEM));
+ lua_pushinteger(L, ENOMEM);
+ return (3);
+ }
+
+ outpath = basename(inpath);
+ lua_pushstring(L, outpath);
+ free(inpath);
+ return (1);
+}
+
+static int
+lua_chmod(lua_State *L)
+{
+ const char *path;
+ mode_t mode;
+
+ enforce_max_args(L, 2);
+ path = luaL_checkstring(L, 1);
+ mode = (mode_t)luaL_checkinteger(L, 2);
+
+ if (chmod(path, mode) == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+ lua_pushinteger(L, 0);
+ return (1);
+}
+
+static int
+lua_chown(lua_State *L)
+{
+ const char *path;
+ uid_t owner = (uid_t)-1;
+ gid_t group = (gid_t)-1;
+ int error;
+
+ enforce_max_args(L, 3);
+
+ path = luaL_checkstring(L, 1);
+ if (lua_isinteger(L, 2))
+ owner = (uid_t)lua_tointeger(L, 2);
+ else if (lua_isstring(L, 2)) {
+ char buf[4096];
+ struct passwd passwd, *pwd;
+
+ error = getpwnam_r(lua_tostring(L, 2), &passwd,
+ buf, sizeof(buf), &pwd);
+ if (error == 0)
+ owner = pwd->pw_uid;
+ else
+ return (luaL_argerror(L, 2,
+ lua_pushfstring(L, "unknown user %s",
+ lua_tostring(L, 2))));
+ } else if (!lua_isnoneornil(L, 2)) {
+ const char *type = luaL_typename(L, 2);
+ return (luaL_argerror(L, 2,
+ lua_pushfstring(L, "integer or string expected, got %s",
+ type)));
+ }
+
+ if (lua_isinteger(L, 3))
+ group = (gid_t)lua_tointeger(L, 3);
+ else if (lua_isstring(L, 3)) {
+ char buf[4096];
+ struct group gr, *grp;
+
+ error = getgrnam_r(lua_tostring(L, 3), &gr, buf, sizeof(buf),
+ &grp);
+ if (error == 0)
+ group = grp->gr_gid;
+ else
+ return (luaL_argerror(L, 3,
+ lua_pushfstring(L, "unknown group %s",
+ lua_tostring(L, 3))));
+ } else if (!lua_isnoneornil(L, 3)) {
+ const char *type = luaL_typename(L, 3);
+ return (luaL_argerror(L, 3,
+ lua_pushfstring(L, "integer or string expected, got %s",
+ type)));
+ }
+
+ if (chown(path, owner, group) == -1) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+ lua_pushinteger(L, 0);
+ return (1);
+}
+
+static int
+lua_pclose(lua_State *L)
+{
+ int error, fd;
+
+ enforce_max_args(L, 1);
+
+ fd = luaL_checkinteger(L, 1);
+ if (fd < 0) {
+ error = EBADF;
+ goto err;
+ }
+
+ if (close(fd) == 0) {
+ lua_pushinteger(L, 0);
+ return (1);
+ }
+
+ error = errno;
+err:
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(error));
+ lua_pushinteger(L, error);
+ return (3);
+
+}
+
+static int
+lua_dup2(lua_State *L)
+{
+ int error, oldd, newd;
+
+ enforce_max_args(L, 2);
+
+ oldd = luaL_checkinteger(L, 1);
+ if (oldd < 0) {
+ error = EBADF;
+ goto err;
+ }
+
+ newd = luaL_checkinteger(L, 2);
+ if (newd < 0) {
+ error = EBADF;
+ goto err;
+ }
+
+ error = dup2(oldd, newd);
+ if (error >= 0) {
+ lua_pushinteger(L, error);
+ return (1);
+ }
+
+ error = errno;
+err:
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(error));
+ lua_pushinteger(L, error);
+ return (3);
+}
+
+static int
+lua_execp(lua_State *L)
+{
+ int argc, error;
+ const char *file;
+ const char **argv;
+
+ enforce_max_args(L, 2);
+
+ file = luaL_checkstring(L, 1);
+ luaL_checktype(L, 2, LUA_TTABLE);
+
+ lua_len(L, 2);
+ argc = lua_tointeger(L, -1);
+
+ /*
+ * Use lua_newuserdatauv() to allocate a scratch buffer that is tracked
+ * and freed by lua's GC. This avoid any chance of a leak if a lua error
+ * is raised later in this function (e.g. by luaL_argerror()).
+ * The (argc + 2) size gives enough space in the buffer for argv[0] and
+ * the terminating NULL.
+ */
+ argv = lua_newuserdatauv(L, (argc + 2) * sizeof(char *), 0);
+
+ /*
+ * Sequential tables in lua start at index 1 by convention.
+ * If there happens to be a string at index 0, use that to
+ * override the default argv[0]. This matches the lposix API.
+ */
+ lua_pushinteger(L, 0);
+ lua_gettable(L, 2);
+ argv[0] = lua_tostring(L, -1);
+ if (argv[0] == NULL) {
+ argv[0] = file;
+ }
+
+ for (int i = 1; i <= argc; i++) {
+ lua_pushinteger(L, i);
+ lua_gettable(L, 2);
+ argv[i] = lua_tostring(L, -1);
+ if (argv[i] == NULL) {
+ luaL_argerror(L, 2,
+ "argv table must contain only strings");
+ }
+ }
+ argv[argc + 1] = NULL;
+
+ execvp(file, (char **)argv);
+ error = errno;
+
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(error));
+ lua_pushinteger(L, error);
+ return (3);
+}
+
+static int
+lua_fnmatch(lua_State *L)
+{
+ const char *pattern, *string;
+ int flags;
+
+ enforce_max_args(L, 3);
+ pattern = luaL_checkstring(L, 1);
+ string = luaL_checkstring(L, 2);
+ flags = luaL_optinteger(L, 3, 0);
+
+ lua_pushinteger(L, fnmatch(pattern, string, flags));
+
+ return (1);
+}
+
+static int
+lua_uname(lua_State *L)
+{
+ struct utsname name;
+ int error;
+
+ enforce_max_args(L, 0);
+
+ error = uname(&name);
+ if (error != 0) {
+ error = errno;
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(error));
+ lua_pushinteger(L, error);
+ return (3);
+ }
+
+ lua_newtable(L);
+#define setkv(f) do { \
+ lua_pushstring(L, name.f); \
+ lua_setfield(L, -2, #f); \
+} while (0)
+ setkv(sysname);
+ setkv(nodename);
+ setkv(release);
+ setkv(version);
+ setkv(machine);
+#undef setkv
+
+ return (1);
+}
+
+static int
+lua_dirname(lua_State *L)
+{
+ char *inpath, *outpath;
+
+ enforce_max_args(L, 1);
+
+ inpath = strdup(luaL_checkstring(L, 1));
+ if (inpath == NULL) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(ENOMEM));
+ lua_pushinteger(L, ENOMEM);
+ return (3);
+ }
+
+ outpath = dirname(inpath);
+ lua_pushstring(L, outpath);
+ free(inpath);
+ return (1);
+}
+
+static int
+lua_fork(lua_State *L)
+{
+ pid_t pid;
+
+ enforce_max_args(L, 0);
+
+ pid = fork();
+ if (pid < 0) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+
+ lua_pushinteger(L, pid);
+ return (1);
+}
+
+static int
+lua_getpid(lua_State *L)
+{
+ enforce_max_args(L, 0);
+
+ lua_pushinteger(L, getpid());
+ return (1);
+}
+
+static int
+lua_pipe(lua_State *L)
+{
+ int error, fd[2];
+
+ enforce_max_args(L, 0);
+
+ error = pipe(fd);
+ if (error != 0) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (1);
+ }
+
+ lua_pushinteger(L, fd[0]);
+ lua_pushinteger(L, fd[1]);
+ return (2);
+}
+
+static int
+lua_read(lua_State *L)
+{
+ char *buf;
+ ssize_t ret;
+ size_t sz;
+ int error, fd;
+
+ enforce_max_args(L, 2);
+ fd = luaL_checkinteger(L, 1);
+ sz = luaL_checkinteger(L, 2);
+
+ if (fd < 0) {
+ error = EBADF;
+ goto err;
+ }
+
+ buf = malloc(sz);
+ if (buf == NULL)
+ goto err;
+
+ /*
+ * For 0-byte reads, we'll still push the empty string and let the
+ * caller deal with EOF to match lposix semantics.
+ */
+ ret = read(fd, buf, sz);
+ if (ret >= 0)
+ lua_pushlstring(L, buf, ret);
+ else if (ret < 0)
+ error = errno; /* Save to avoid clobber by free() */
+
+ free(buf);
+ if (error != 0)
+ goto err;
+
+ /* Just the string pushed. */
+ return (1);
+err:
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(error));
+ lua_pushinteger(L, error);
+ return (3);
+}
+
+static int
+lua_realpath(lua_State *L)
+{
+ const char *inpath;
+ char *outpath;
+
+ enforce_max_args(L, 1);
+ inpath = luaL_checkstring(L, 1);
+
+ outpath = realpath(inpath, NULL);
+ if (outpath == NULL) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+
+ lua_pushstring(L, outpath);
+ free(outpath);
+ return (1);
+}
+
+static int
+lua_wait(lua_State *L)
+{
+ pid_t pid;
+ int options, status;
+
+ enforce_max_args(L, 2);
+ pid = luaL_optinteger(L, 1, -1);
+ options = luaL_optinteger(L, 2, 0);
+
+ status = 0;
+ pid = waitpid(pid, &status, options);
+ if (pid < 0) {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushinteger(L, errno);
+ return (3);
+ }
+
+ lua_pushinteger(L, pid);
+ if (pid == 0) {
+ lua_pushliteral(L, "running");
+ return (2);
+ }
+
+ if (WIFCONTINUED(status)) {
+ lua_pushliteral(L, "continued");
+ return (2);
+ } else if(WIFSTOPPED(status)) {
+ lua_pushliteral(L, "stopped");
+ lua_pushinteger(L, WSTOPSIG(status));
+ return (3);
+ } else if (WIFEXITED(status)) {
+ lua_pushliteral(L, "exited");
+ lua_pushinteger(L, WEXITSTATUS(status));
+ return (3);
+ } else if (WIFSIGNALED(status)) {
+ lua_pushliteral(L, "killed");
+ lua_pushinteger(L, WTERMSIG(status));
+ return (3);
+ }
+
+ return (1);
+}
+
+static int
+lua_write(lua_State *L)
+{
+ const char *buf;
+ size_t bufsz, sz;
+ ssize_t ret;
+ off_t offset;
+ int error, fd;
+
+ enforce_max_args(L, 4);
+
+ fd = luaL_checkinteger(L, 1);
+ if (fd < 0) {
+ error = EBADF;
+ goto err;
+ }
+
+ buf = luaL_checkstring(L, 2);
+
+ bufsz = lua_rawlen(L, 2);
+ sz = luaL_optinteger(L, 3, bufsz);
+
+ offset = luaL_optinteger(L, 4, 0);
+
+
+ if ((size_t)offset > bufsz || offset + sz > bufsz) {
+ lua_pushnil(L);
+ lua_pushfstring(L,
+ "write: invalid access offset %zu, size %zu in a buffer size %zu",
+ offset, sz, bufsz);
+ lua_pushinteger(L, EINVAL);
+ return (3);
+ }
+
+ ret = write(fd, buf + offset, sz);
+ if (ret < 0) {
+ error = errno;
+ goto err;
+ }
+
+ lua_pushinteger(L, ret);
+ return (1);
+err:
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(error));
+ lua_pushinteger(L, error);
+ return (3);
+}
+
+#define REG_DEF(n, func) { #n, func }
+#define REG_SIMPLE(n) REG_DEF(n, lua_ ## n)
+static const struct luaL_Reg libgenlib[] = {
+ REG_SIMPLE(basename),
+ REG_SIMPLE(dirname),
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg stdliblib[] = {
+ REG_SIMPLE(realpath),
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg fnmatchlib[] = {
+ REG_SIMPLE(fnmatch),
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg sys_statlib[] = {
+ REG_SIMPLE(chmod),
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg sys_utsnamelib[] = {
+ REG_SIMPLE(uname),
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg sys_waitlib[] = {
+ REG_SIMPLE(wait),
+ {NULL, NULL},
+};
+
+static const struct luaL_Reg unistdlib[] = {
+ REG_SIMPLE(_exit),
+ REG_SIMPLE(chown),
+ REG_DEF(close, lua_pclose),
+ REG_SIMPLE(dup2),
+ REG_SIMPLE(execp),
+ REG_SIMPLE(fork),
+ REG_SIMPLE(getpid),
+ REG_SIMPLE(pipe),
+ REG_SIMPLE(read),
+ REG_SIMPLE(write),
+ { NULL, NULL },
+};
+
+#undef REG_SIMPLE
+#undef REG_DEF
+
+static int
+luaopen_posix_libgen(lua_State *L)
+{
+ luaL_newlib(L, libgenlib);
+ return (1);
+}
+
+static int
+luaopen_posix_stdlib(lua_State *L)
+{
+ luaL_newlib(L, stdliblib);
+ return (1);
+}
+
+static int
+luaopen_posix_fnmatch(lua_State *L)
+{
+ luaL_newlib(L, fnmatchlib);
+
+#define setkv(f) do { \
+ lua_pushinteger(L, f); \
+ lua_setfield(L, -2, #f); \
+} while (0)
+ setkv(FNM_PATHNAME);
+ setkv(FNM_NOESCAPE);
+ setkv(FNM_NOMATCH);
+ setkv(FNM_PERIOD);
+#undef setkv
+
+ return 1;
+}
+
+static int
+luaopen_posix_sys_stat(lua_State *L)
+{
+ luaL_newlib(L, sys_statlib);
+ return (1);
+}
+
+static int
+luaopen_posix_sys_utsname(lua_State *L)
+{
+ luaL_newlib(L, sys_utsnamelib);
+ return 1;
+}
+
+static int
+luaopen_posix_sys_wait(lua_State *L)
+{
+ luaL_newlib(L, sys_waitlib);
+
+#define lua_pushflag(L, flag) do { \
+ lua_pushinteger(L, flag); \
+ lua_setfield(L, -2, #flag); \
+} while(0)
+
+ /* Only these two exported by lposix */
+ lua_pushflag(L, WNOHANG);
+ lua_pushflag(L, WUNTRACED);
+
+ lua_pushflag(L, WCONTINUED);
+ lua_pushflag(L, WSTOPPED);
+#ifdef WTRAPPED
+ lua_pushflag(L, WTRAPPED);
+#endif
+ lua_pushflag(L, WEXITED);
+ lua_pushflag(L, WNOWAIT);
+#undef lua_pushflag
+
+ return (1);
+}
+
+static int
+luaopen_posix_unistd(lua_State *L)
+{
+ luaL_newlib(L, unistdlib);
+ return (1);
+}
+
+int
+luaopen_posix(lua_State *L)
+{
+ lua_newtable(L); /* posix */
+
+ luaL_requiref(L, "posix.fnmatch", luaopen_posix_fnmatch, 0);
+ lua_setfield(L, -2, "fnmatch");
+
+ luaL_requiref(L, "posix.libgen", luaopen_posix_libgen, 0);
+ lua_setfield(L, -2, "libgen");
+
+ luaL_requiref(L, "posix.stdlib", luaopen_posix_stdlib, 0);
+ lua_setfield(L, -2, "stdlib");
+
+ lua_newtable(L); /* posix.sys */
+ luaL_requiref(L, "posix.sys.stat", luaopen_posix_sys_stat, 0);
+ lua_setfield(L, -2, "stat");
+ luaL_requiref(L, "posix.sys.utsname", luaopen_posix_sys_utsname, 0);
+ lua_setfield(L, -2, "utsname");
+ luaL_requiref(L, "posix.sys.wait", luaopen_posix_sys_wait, 0);
+ lua_setfield(L, -2, "wait");
+ lua_setfield(L, -2, "sys");
+
+ luaL_requiref(L, "posix.unistd", luaopen_posix_unistd, 0);
+ lua_setfield(L, -2, "unistd");
+
+ return (1);
+}
diff --git a/libexec/flua/modules/lposix.h b/libexec/flua/modules/lposix.h
new file mode 100644
index 000000000000..1aa33f042571
--- /dev/null
+++ b/libexec/flua/modules/lposix.h
@@ -0,0 +1,10 @@
+/*-
+ *
+ * This file is in the public domain.
+ */
+
+#pragma once
+
+#include <lua.h>
+
+int luaopen_posix(lua_State *L);
diff --git a/libexec/getty/Makefile b/libexec/getty/Makefile
new file mode 100644
index 000000000000..18668bee6aa0
--- /dev/null
+++ b/libexec/getty/Makefile
@@ -0,0 +1,12 @@
+PACKAGE= runtime
+
+CONFS= gettytab
+PROG= getty
+SRCS= main.c init.c subr.c chat.c
+LIBADD= util
+MAN= gettytab.5 ttys.5 getty.8
+
+WFORMAT=0
+
+.include <bsd.prog.mk>
+
diff --git a/libexec/getty/Makefile.depend b/libexec/getty/Makefile.depend
new file mode 100644
index 000000000000..678747db6f2c
--- /dev/null
+++ b/libexec/getty/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/getty/chat.c b/libexec/getty/chat.c
new file mode 100644
index 000000000000..f1fa6f504b85
--- /dev/null
+++ b/libexec/getty/chat.c
@@ -0,0 +1,485 @@
+/*-
+ * Copyright (c) 1997
+ * David L Nugent <davidn@blaze.net.au>.
+ * All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Modem chat module - send/expect style functions for getty
+ * For semi-intelligent modem handling.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+
+#include <ctype.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "gettytab.h"
+#include "extern.h"
+
+#define PAUSE_CH (unsigned char)'\xff' /* pause kludge */
+
+#define CHATDEBUG_RECEIVE 0x01
+#define CHATDEBUG_SEND 0x02
+#define CHATDEBUG_EXPECT 0x04
+#define CHATDEBUG_MISC 0x08
+
+#define CHATDEBUG_DEFAULT 0
+#define CHAT_DEFAULT_TIMEOUT 10
+
+
+static int chat_debug = CHATDEBUG_DEFAULT;
+static int chat_alarm = CHAT_DEFAULT_TIMEOUT; /* Default */
+
+static volatile int alarmed = 0;
+
+
+static void chat_alrm(int);
+static int chat_unalarm(void);
+static int getdigit(char **, int, int);
+static char **read_chat(char **);
+static char *cleanchr(char **, unsigned char);
+static const char *cleanstr(const char *, int);
+static const char *result(int);
+static int chat_expect(const char *);
+static int chat_send(char const *);
+
+
+/*
+ * alarm signal handler
+ * handle timeouts in read/write
+ * change stdin to non-blocking mode to prevent
+ * possible hang in read().
+ */
+
+static void
+chat_alrm(int signo __unused)
+{
+ int on = 1;
+
+ alarm(1);
+ alarmed = 1;
+ signal(SIGALRM, chat_alrm);
+ ioctl(STDIN_FILENO, FIONBIO, &on);
+}
+
+
+/*
+ * Turn back on blocking mode reset by chat_alrm()
+ */
+
+static int
+chat_unalarm(void)
+{
+ int off = 0;
+ return ioctl(STDIN_FILENO, FIONBIO, &off);
+}
+
+
+/*
+ * convert a string of a given base (octal/hex) to binary
+ */
+
+static int
+getdigit(char **ptr, int base, int max)
+{
+ int i, val = 0;
+ char * q;
+
+ static const char xdigits[] = "0123456789abcdef";
+
+ for (i = 0, q = *ptr; i++ < max; ++q) {
+ int sval;
+ const char * s = strchr(xdigits, tolower(*q));
+
+ if (s == NULL || (sval = s - xdigits) >= base)
+ break;
+ val = (val * base) + sval;
+ }
+ *ptr = q;
+ return val;
+}
+
+
+/*
+ * read_chat()
+ * Convert a whitespace delimtied string into an array
+ * of strings, being expect/send pairs
+ */
+
+static char **
+read_chat(char **chatstr)
+{
+ char *str = *chatstr;
+ char **res = NULL;
+
+ if (str != NULL) {
+ char *tmp = NULL;
+ int l;
+
+ if ((l=strlen(str)) > 0 && (tmp=malloc(l + 1)) != NULL &&
+ (res=malloc(((l + 1) / 2 + 1) * sizeof(char *))) != NULL) {
+ static char ws[] = " \t";
+ char * p;
+
+ for (l = 0, p = strtok(strcpy(tmp, str), ws);
+ p != NULL;
+ p = strtok(NULL, ws))
+ {
+ char *q, *r;
+
+ /* Read escapes */
+ for (q = r = p; *r; ++q)
+ {
+ if (*q == '\\')
+ {
+ /* handle special escapes */
+ switch (*++q)
+ {
+ case 'a': /* bell */
+ *r++ = '\a';
+ break;
+ case 'r': /* cr */
+ *r++ = '\r';
+ break;
+ case 'n': /* nl */
+ *r++ = '\n';
+ break;
+ case 'f': /* ff */
+ *r++ = '\f';
+ break;
+ case 'b': /* bs */
+ *r++ = '\b';
+ break;
+ case 'e': /* esc */
+ *r++ = 27;
+ break;
+ case 't': /* tab */
+ *r++ = '\t';
+ break;
+ case 'p': /* pause */
+ *r++ = PAUSE_CH;
+ break;
+ case 's':
+ case 'S': /* space */
+ *r++ = ' ';
+ break;
+ case 'x': /* hexdigit */
+ ++q;
+ *r++ = getdigit(&q, 16, 2);
+ --q;
+ break;
+ case '0': /* octal */
+ ++q;
+ *r++ = getdigit(&q, 8, 3);
+ --q;
+ break;
+ default: /* literal */
+ *r++ = *q;
+ break;
+ case 0: /* not past eos */
+ --q;
+ break;
+ }
+ } else {
+ /* copy standard character */
+ *r++ = *q;
+ }
+ }
+
+ /* Remove surrounding quotes, if any
+ */
+ if (*p == '"' || *p == '\'') {
+ q = strrchr(p+1, *p);
+ if (q != NULL && *q == *p && q[1] == '\0') {
+ *q = '\0';
+ p++;
+ }
+ }
+
+ res[l++] = p;
+ }
+ res[l] = NULL;
+ *chatstr = tmp;
+ return res;
+ }
+ free(tmp);
+ }
+ return res;
+}
+
+
+/*
+ * clean a character for display (ctrl/meta character)
+ */
+
+static char *
+cleanchr(char **buf, unsigned char ch)
+{
+ int l;
+ static char tmpbuf[5];
+ char * tmp = buf ? *buf : tmpbuf;
+
+ if (ch & 0x80) {
+ strcpy(tmp, "M-");
+ l = 2;
+ ch &= 0x7f;
+ } else
+ l = 0;
+
+ if (ch < 32) {
+ tmp[l++] = '^';
+ tmp[l++] = ch + '@';
+ } else if (ch == 127) {
+ tmp[l++] = '^';
+ tmp[l++] = '?';
+ } else
+ tmp[l++] = ch;
+ tmp[l] = '\0';
+
+ if (buf)
+ *buf = tmp + l;
+ return tmp;
+}
+
+
+/*
+ * clean a string for display (ctrl/meta characters)
+ */
+
+static const char *
+cleanstr(const char *s, int l)
+{
+ static char * tmp = NULL;
+ static int tmplen = 0;
+
+ if (tmplen < l * 4 + 1)
+ tmp = realloc(tmp, tmplen = l * 4 + 1);
+
+ if (tmp == NULL) {
+ tmplen = 0;
+ return "(mem alloc error)";
+ } else {
+ int i = 0;
+ char * p = tmp;
+
+ while (i < l)
+ cleanchr(&p, s[i++]);
+ *p = '\0';
+ }
+
+ return tmp;
+}
+
+
+/*
+ * return result as a pseudo-english word
+ */
+
+static const char *
+result(int r)
+{
+ static const char * results[] = {
+ "OK", "MEMERROR", "IOERROR", "TIMEOUT"
+ };
+ return results[r & 3];
+}
+
+
+/*
+ * chat_expect()
+ * scan input for an expected string
+ */
+
+static int
+chat_expect(const char *str)
+{
+ int len, r = 0;
+
+ if (chat_debug & CHATDEBUG_EXPECT)
+ syslog(LOG_DEBUG, "chat_expect '%s'", cleanstr(str, strlen(str)));
+
+ if ((len = strlen(str)) > 0) {
+ int i = 0;
+ char * got;
+
+ if ((got = malloc(len + 1)) == NULL)
+ r = 1;
+ else {
+
+ memset(got, 0, len+1);
+ alarm(chat_alarm);
+ alarmed = 0;
+
+ while (r == 0 && i < len) {
+ if (alarmed)
+ r = 3;
+ else {
+ unsigned char ch;
+
+ if (read(STDIN_FILENO, &ch, 1) == 1) {
+
+ if (chat_debug & CHATDEBUG_RECEIVE)
+ syslog(LOG_DEBUG, "chat_recv '%s' m=%d",
+ cleanchr(NULL, ch), i);
+
+ if (ch == str[i])
+ got[i++] = ch;
+ else if (i > 0) {
+ int j = 1;
+
+ /* See if we can resync on a
+ * partial match in our buffer
+ */
+ while (j < i && memcmp(got + j, str, i - j) != 0)
+ j++;
+ if (j < i)
+ memcpy(got, got + j, i - j);
+ i -= j;
+ }
+ } else
+ r = alarmed ? 3 : 2;
+ }
+ }
+ alarm(0);
+ chat_unalarm();
+ alarmed = 0;
+ free(got);
+ }
+ }
+
+ if (chat_debug & CHATDEBUG_EXPECT)
+ syslog(LOG_DEBUG, "chat_expect %s", result(r));
+
+ return r;
+}
+
+
+/*
+ * chat_send()
+ * send a chat string
+ */
+
+static int
+chat_send(char const *str)
+{
+ int r = 0;
+
+ if (chat_debug & CHATDEBUG_SEND)
+ syslog(LOG_DEBUG, "chat_send '%s'", cleanstr(str, strlen(str)));
+
+ if (*str) {
+ alarm(chat_alarm);
+ alarmed = 0;
+ while (r == 0 && *str)
+ {
+ unsigned char ch = (unsigned char)*str++;
+
+ if (alarmed)
+ r = 3;
+ else if (ch == PAUSE_CH)
+ usleep(500000); /* 1/2 second */
+ else {
+ usleep(10000); /* be kind to modem */
+ if (write(STDOUT_FILENO, &ch, 1) != 1)
+ r = alarmed ? 3 : 2;
+ }
+ }
+ alarm(0);
+ chat_unalarm();
+ alarmed = 0;
+ }
+
+ if (chat_debug & CHATDEBUG_SEND)
+ syslog(LOG_DEBUG, "chat_send %s", result(r));
+
+ return r;
+}
+
+
+/*
+ * getty_chat()
+ *
+ * Termination codes:
+ * -1 - no script supplied
+ * 0 - script terminated correctly
+ * 1 - invalid argument, expect string too large, etc.
+ * 2 - error on an I/O operation or fatal error condition
+ * 3 - timeout waiting for a simple string
+ *
+ * Parameters:
+ * char *scrstr - unparsed chat script
+ * timeout - seconds timeout
+ * debug - debug value (bitmask)
+ */
+
+int
+getty_chat(char *scrstr, int timeout, int debug)
+{
+ int r = -1;
+
+ chat_alarm = timeout ? timeout : CHAT_DEFAULT_TIMEOUT;
+ chat_debug = debug;
+
+ if (scrstr != NULL) {
+ char **script;
+
+ if (chat_debug & CHATDEBUG_MISC)
+ syslog(LOG_DEBUG, "getty_chat script='%s'", scrstr);
+
+ if ((script = read_chat(&scrstr)) != NULL) {
+ int i = r = 0;
+ int off = 0;
+ sig_t old_alarm;
+
+ /*
+ * We need to be in raw mode for all this
+ * Rely on caller...
+ */
+
+ old_alarm = signal(SIGALRM, chat_alrm);
+ chat_unalarm(); /* Force blocking mode at start */
+
+ /*
+ * This is the send/expect loop
+ */
+ while (r == 0 && script[i] != NULL)
+ if ((r = chat_expect(script[i++])) == 0 && script[i] != NULL)
+ r = chat_send(script[i++]);
+
+ signal(SIGALRM, old_alarm);
+ free(script);
+ free(scrstr);
+
+ /*
+ * Ensure stdin is in blocking mode
+ */
+ ioctl(STDIN_FILENO, FIONBIO, &off);
+ }
+
+ if (chat_debug & CHATDEBUG_MISC)
+ syslog(LOG_DEBUG, "getty_chat %s", result(r));
+
+ }
+ return r;
+}
diff --git a/libexec/getty/extern.h b/libexec/getty/extern.h
new file mode 100644
index 000000000000..386eef5d16a3
--- /dev/null
+++ b/libexec/getty/extern.h
@@ -0,0 +1,56 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct delayval;
+struct termios;
+
+extern char **environ;
+extern char editedhost[];
+extern char hostname[];
+extern struct termios tmode, omode;
+extern struct gettyflags gettyflags[];
+extern struct gettynums gettynums[];
+extern struct gettystrs gettystrs[];
+
+int adelay(int, struct delayval *);
+const char *autobaud(void);
+int delaybits(void);
+void edithost(const char *);
+void gendefaults(void);
+void gettable(const char *);
+void makeenv(char *[]);
+const char *portselector(void);
+void set_ttydefaults(int);
+void setchars(void);
+void setdefaults(void);
+void set_flags(int);
+int speed(int);
+int getty_chat(char *, int, int);
diff --git a/libexec/getty/getty.8 b/libexec/getty/getty.8
new file mode 100644
index 000000000000..baed4b861a8d
--- /dev/null
+++ b/libexec/getty/getty.8
@@ -0,0 +1,122 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\" "
+.Dd July 21, 2020
+.Dt GETTY 8
+.Os
+.Sh NAME
+.Nm getty
+.Nd set terminal mode
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Ar type
+.Op Ar tty
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility is called by
+.Xr init 8
+to open and initialize the tty line, read a login name, and invoke
+.Xr login 1 .
+.Pp
+The argument
+.Ar tty
+is the special device file in
+.Pa /dev
+to open for the terminal (for example, ``ttyh0'').
+If there is no argument or the argument is
+.Sq Fl ,
+the tty line is assumed to be open as file descriptor 0.
+.Pp
+The
+.Ar type
+argument can be used to make
+.Nm
+treat the terminal line specially.
+This argument is used as an index into the
+.Xr gettytab 5
+database, to determine the characteristics of the line.
+If there is no argument, or there is no such table, the
+.Em default
+table is used.
+If there is no
+.Pa /etc/gettytab
+a set of system defaults is used.
+If indicated by the table located,
+.Nm
+will clear the terminal screen,
+print a banner heading,
+and prompt for a login name.
+Usually either the banner or the login prompt will include
+the system hostname.
+.Pp
+Most of the default actions of
+.Nm
+can be circumvented, or modified, by a suitable
+.Pa gettytab
+table.
+.Pp
+The
+.Nm
+utility can be set to timeout after some interval,
+which will cause dial up lines to hang up
+if the login name is not entered reasonably quickly.
+.Sh FILES
+.Bl -tag -width /etc/gettytab -compact
+.It Pa /etc/gettytab
+.It Pa /etc/ttys
+.El
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "ttyxx: No such device or address."
+.It "ttyxx: No such file or address."
+.Pp
+A terminal which is turned
+on in the
+.Pa ttys
+file cannot be opened, likely because the requisite
+lines are either not configured into the system, the associated device
+was not attached during boot-time system configuration,
+or the special file in
+.Pa /dev
+does not exist.
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr ioctl 2 ,
+.Xr tty 4 ,
+.Xr gettytab 5 ,
+.Xr ttys 5 ,
+.Xr init 8 ,
+.Xr pstat 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v3 .
diff --git a/libexec/getty/gettytab b/libexec/getty/gettytab
new file mode 100644
index 000000000000..c2a8d9dc5fad
--- /dev/null
+++ b/libexec/getty/gettytab
@@ -0,0 +1,236 @@
+# Most of the table entries here are just copies of the old getty table,
+# it is by no means certain, or even likely, that any of them are optimal
+# for any purpose whatever. Nor is it likely that more than a couple are
+# even correct.
+#
+# The default gettytab entry, used to set defaults for all other
+# entries, and in cases where getty is called with no table name.
+#
+# cb, ce and ck are desirable on most crt's. The non-crt entries need to
+# be changed to turn them off (:cb@:ce@:ck@:).
+#
+# lc should always be on; it's a remainder of some stone age when there
+# have been terminals around not being able of handling lower-case
+# characters. Those terminals aren't supported any longer, but getty is
+# `smart' about them by default.
+#
+# Parity defaults to even, but the Pc entry and all the `std' entries
+# specify no parity. The different parities are:
+# (none): same as ep for getty. login will use terminal as is.
+# ep: getty will use raw mode (cs8 -parenb) (unless rw is set) and
+# fake parity. login will use even parity (cs7 parenb -parodd).
+# op: same as ep except odd parity (cs7 parenb parodd) for login.
+# getty will fake odd parity as well.
+# ap: same as ep except -inpck instead of inpck for login.
+# ap overrides op and ep.
+# np: 1. don't fake parity in getty. The fake parity garbles
+# characters on non-terminals (like pccons) that don't
+# support parity. It would probably better for getty not to
+# try to fake parity. It could just use cbreak mode so as
+# not to force cs8 and let the hardware handle the parity.
+# login has to be rely on the hardware anyway.
+# 2. set cs8 -parenb -istrip -inpck.
+# ep:op: same as ap.
+#
+default:\
+ :cb:ce:ck:lc:fd#1000:im=\r\n%s/%m (%h) (%t)\r\n\r\n:sp#1200:\
+ :if=/etc/issue:
+
+#
+# Fixed speed entries
+#
+# The "std.NNN" names are known to the special case
+# portselector code in getty, however they can
+# be assigned to any table desired.
+# The "NNN-baud" names are known to the special case
+# autobaud code in getty, and likewise can
+# be assigned to any table desired (hopefully the same speed).
+#
+std:\
+ :np:sp#0:
+a|std.110|110-baud:\
+ :np:nd#1:cd#1:uc:sp#110:
+b|std.134|134.5-baud:\
+ :np:nd#1:cd#2:ff#1:td#1:sp#134:ht:nl:
+1|std.150|150-baud:\
+ :np:nd#1:cd#2:td#1:fd#1:sp#150:ht:nl:lm=\E\72\6\6\17login\72 :
+c|std.300|300-baud:\
+ :np:nd#1:cd#1:sp#300:
+d|std.600|600-baud:\
+ :np:nd#1:cd#1:sp#600:
+f|std.1200|1200-baud:\
+ :np:fd#1:sp#1200:
+6|std.2400|2400-baud:\
+ :np:sp#2400:
+7|std.4800|4800-baud:\
+ :np:sp#4800:
+2|std.9600|9600-baud:\
+ :np:sp#9600:
+g|std.19200|19200-baud:\
+ :np:sp#19200:
+std.38400|38400-baud:\
+ :np:sp#38400:
+std.57600|57600-baud:\
+ :np:sp#57600:
+std.115200|115200-baud:\
+ :np:sp#115200:
+std.230400|230400-baud:\
+ :np:sp#230400:
+
+#
+# Entry specifying explicit device settings. See termios(4) and
+# /usr/include/termios.h, too. The entry forces the tty into
+# CLOCAL mode (so no DCD is required), and uses Xon/Xoff flow control.
+#
+# cflags: CLOCAL | HUPCL | CREAD | CS8
+# oflags: OPOST | ONLCR | OXTABS
+# iflags: IXOFF | IXON | ICRNL | IGNPAR
+# lflags: IEXTEN | ICANON | ISIG | ECHOCTL | ECHO | ECHOK | ECHOE | ECHOKE
+#
+# The `0' flags don't have input enabled. The `1' flags don't echo.
+# (Echoing is done inside getty itself.)
+#
+local.9600|CLOCAL tty @ 9600 Bd:\
+ :c0#0x0000c300:c1#0x0000cb00:c2#0x0000cb00:\
+ :o0#0x00000007:o1#0x00000002:o2#0x00000007:\
+ :i0#0x00000704:i1#0x00000000:i2#0x00000704:\
+ :l0#0x000005cf:l1#0x00000000:l2#0x000005cf:\
+ :sp#9600:np:
+
+#
+# Dial in rotary tables, speed selection via 'break'
+#
+0|d300|Dial-300:\
+ :nx=d1200:cd#2:sp#300:
+d1200|Dial-1200:\
+ :nx=d150:fd#1:sp#1200:
+d150|Dial-150:\
+ :nx=d110:lm@:tc=150-baud:
+d110|Dial-110:\
+ :nx=d300:tc=300-baud:
+
+#
+# Fast dialup terminals, 2400/1200/300 rotary (can start either way)
+#
+D2400|d2400|Fast-Dial-2400:\
+ :nx=D1200:tc=2400-baud:
+3|D1200|Fast-Dial-1200:\
+ :nx=D300:tc=1200-baud:
+5|D300|Fast-Dial-300:\
+ :nx=D2400:tc=300-baud:
+
+#
+#telebit (19200)
+#
+t19200:\
+ :nx=t2400:tc=19200-baud:
+t2400:\
+ :nx=t1200:tc=2400-baud:
+t1200:\
+ :nx=t19200:tc=1200-baud:
+
+#
+#telebit (9600)
+#
+t9600:\
+ :nx=t2400a:tc=9600-baud:
+t2400a:\
+ :nx=t1200a:tc=2400-baud:
+t1200a:\
+ :nx=t9600:tc=1200-baud:
+
+#
+# Odd special case terminals
+#
+-|tty33|asr33|Pity the poor user of this beast:\
+ :tc=110-baud:
+
+4|Console|Console Decwriter II:\
+ :nd@:cd@:rw:tc=300-baud:
+
+e|Console-1200|Console Decwriter III:\
+ :fd@:nd@:cd@:rw:tc=1200-baud:
+
+i|Interdata console:\
+ :uc:sp#0:
+
+l|lsi chess terminal:\
+ :sp#300:
+
+X|Xwindow|X window system:\
+ :fd@:nd@:cd@:rw:sp#9600:
+
+P|Pc|Pc console:\
+ :ht:np:sp#9600:
+
+#
+# Weirdo special case for fast crt's with hardcopy devices
+#
+8|T9600|CRT with hardcopy:\
+ :nx=T300:tc=9600-baud:
+9|T300|CRT with hardcopy (300):\
+ :nx=T9600:tc=300-baud:
+
+#
+# Plugboard, and misc other terminals
+#
+plug-9600|Plugboard-9600:\
+ :pf#1:tc=9600-baud:
+p|P9600|Plugboard-9600-rotary:\
+ :pf#1:nx=P300:tc=9600-baud:
+q|P300|Plugboard-300:\
+ :pf#1:nx=P1200:tc=300-baud:
+r|P1200|Plugboard-1200:\
+ :pf#1:nx=P9600:tc=1200-baud:
+
+#
+# XXXX Port selector
+#
+s|DSW|Port Selector:\
+ :ps:sp#2400:
+
+#
+# Auto-baud speed detect entry for Micom 600.
+# Special code in getty will switch this out
+# to one of the NNN-baud entries.
+#
+A|Auto-baud:\
+ :ab:sp#2400:f0#040:
+
+#
+# autologin - automatically log in as root
+#
+
+autologin|al.9600:\
+ :al=root:tc=std.9600:
+al.19200:\
+ :al=root:tc=std.19200:
+al.38400:\
+ :al=root:tc=std.38400:
+al.57600:\
+ :al=root:tc=std.57600:
+al.115200:\
+ :al=root:tc=std.115200:
+al.230400:\
+ :al=root:tc=std.230400:
+al.Pc:\
+ :al=root:tc=Pc
+
+#
+# Entries for 3-wire serial terminals. These don't supply carrier, so
+# clocal needs to be set, and crtscts needs to be unset.
+#
+3wire:\
+ :np:nc:sp#0:
+3wire.9600|9600-3wire:\
+ :np:nc:sp#9600:
+3wire.19200|19200-3wire:\
+ :np:nc:sp#19200:
+3wire.38400|38400-3wire:\
+ :np:nc:sp#38400:
+3wire.57600|57600-3wire:\
+ :np:nc:sp#57600:
+3wire.115200|115200-3wire:\
+ :np:nc:sp#115200:
+3wire.230400|230400-3wire:\
+ :np:nc:sp#230400:
diff --git a/libexec/getty/gettytab.5 b/libexec/getty/gettytab.5
new file mode 100644
index 000000000000..1fb769efebc1
--- /dev/null
+++ b/libexec/getty/gettytab.5
@@ -0,0 +1,515 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\" "
+.Dd September 29, 2022
+.Dt GETTYTAB 5
+.Os
+.Sh NAME
+.Nm gettytab
+.Nd terminal configuration data base
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file
+is a simplified version of the
+.Xr termcap 5
+data base
+used to describe terminal lines.
+The initial terminal login process
+.Xr getty 8
+accesses the
+.Nm
+file each time it starts, allowing simpler
+reconfiguration of terminal characteristics.
+Each entry in the data base
+is used to describe one class of terminals.
+.Pp
+There is a default terminal class,
+.Va default ,
+that is used to set global defaults for all other classes.
+(That is, the
+.Va default
+entry is read, then the entry for the class required
+is used to override particular settings.)
+.Sh CAPABILITIES
+Refer to
+.Xr termcap 5
+for a description of the file layout.
+The
+.Va default
+column below lists defaults obtained if there is
+no entry in the table obtained, nor one in the special
+.Va default
+table.
+.Bl -column Name Type /usr/bin/login
+.It Sy "Name Type Default Description"
+.It "ac str unused expect-send chat script for modem answer"
+.It "al str unused user to auto-login instead of prompting"
+.It "ap bool false terminal uses any parity"
+.It "bk str 0377 alternate end of line character (input break)"
+.It "c0 num unused tty control flags to write messages"
+.It "c1 num unused tty control flags to read login name"
+.It "c2 num unused tty control flags to leave terminal as"
+.It "ce bool false use crt erase algorithm"
+.It "ck bool false use crt kill algorithm"
+.It "cl str" Ta Dv NULL
+.Ta No "screen clear sequence"
+.It "co bool false console - add"
+.Ql \en
+after login prompt
+.It "ct num 10 chat timeout for"
+.Va \&ac
+and
+.Va \&ic
+scripts
+.It "dc num 0 chat debug bitmask"
+.It "de num 0 delay secs and flush input before writing first prompt"
+.It "df str %+ the" Xr strftime 3 "format used for \&%d in the banner message"
+.It "ds str" Ta So Li ^Y
+.Sc Ta No "delayed suspend character"
+.It "dx bool false set"
+.Dv DECCTLQ
+.It "ec bool false leave echo"
+.Em OFF
+.It "ep bool false terminal uses even parity"
+.It "er str" Ta So Li ^?
+.Sc Ta No "erase character"
+.It "et str" Ta So Li ^D
+.Sc Ta No "end of text"
+.Pq Dv EOF
+character
+.It "ev str" Ta Dv NULL
+.Ta No "initial environment"
+.It "fl str" Ta So Li ^O
+.Sc Ta No "output flush character"
+.It "hc bool false do"
+.Em NOT
+hangup line on last close
+.It "he str" Ta Dv NULL
+.Ta No "hostname editing regular expression"
+.It "hn str hostname hostname"
+.It "ht bool false terminal has real tabs"
+.It "hw bool false do cts/rts hardware flow control"
+.It "i0 num unused tty input flags to write messages"
+.It "i1 num unused tty input flags to read login name"
+.It "i2 num unused tty input flags to leave terminal as"
+.It "ic str unused expect-send chat script for modem initialization"
+.It "if str unused display named file before prompt, like /etc/issue"
+.It "ig bool false ignore garbage characters in login name"
+.It "im str" Ta Dv NULL
+.Ta No "initial (banner) message"
+.It "iM str" Ta Dv NULL
+.Ta No "execute named file to generate initial (banner) message"
+.It "in str" Ta So Li ^C
+.Sc Ta No "interrupt character"
+.It "is num unused input speed"
+.It "kl str" Ta So Li ^U
+.Sc Ta No "kill character"
+.It "l0 num unused tty local flags to write messages"
+.It "l1 num unused tty local flags to read login name"
+.It "l2 num unused tty local flags to leave terminal as"
+.It "lm str login: login prompt"
+.It "ln str" Ta So Li ^V
+.Sc Ta No "``literal next'' character"
+.It "lo str" Ta Pa /usr/bin/login
+.Ta No "program to exec when name obtained"
+.It "mb bool false do flow control based on carrier"
+.It "nc bool false terminal does not supply carrier (set clocal)"
+.It "nl bool false terminal has (or might have) a newline character"
+.It "np bool false terminal uses no parity (i.e., 8-bit characters)"
+.It "nx str default next table (for auto speed selection)"
+.It "o0 num unused tty output flags to write messages"
+.It "o1 num unused tty output flags to read login name"
+.It "o2 num unused tty output flags to leave terminal as"
+.It "op bool false terminal uses odd parity"
+.It "os num unused output speed"
+.It "pc str" Ta So Li \e0
+.Sc Ta No "pad character"
+.It "pe bool false use printer (hard copy) erase algorithm"
+.It "pf num 0 delay"
+between first prompt and following flush (seconds)
+.It "pl bool false start PPP login program unconditionally if"
+.Va \&pp
+is specified
+.It "pp str unused PPP login program"
+.It "ps bool false line connected to a"
+.Tn MICOM
+port selector
+.It "qu str" Ta So Li \&^\e
+.Sc Ta No "quit character"
+.It "rp str" Ta So Li ^R
+.Sc Ta No "line retype character"
+.It "rt num unused ring timeout when using"
+.Va \&ac
+.It "rw bool false do"
+.Em NOT
+use raw for input, use cbreak
+.It "sp num unused line speed (input and output)"
+.It "su str" Ta So Li ^Z
+.Sc Ta No "suspend character"
+.It "tc str none table continuation"
+.It "to num 0 timeout (seconds)"
+.It "tt str" Ta Dv NULL
+.Ta No "terminal type (for environment)"
+.It "ub bool false do unbuffered output (of prompts etc)"
+.It "we str" Ta So Li ^W
+.Sc Ta No "word erase character"
+.It "xc bool false do"
+.Em NOT
+echo control chars as
+.Ql ^X
+.It "xf str" Ta So Li ^S Sc Ta Dv XOFF
+(stop output) character
+.It "xn str" Ta So Li ^Q Sc Ta Dv XON
+(start output) character
+.It "Lo str C the locale name used for \&%d in the banner message"
+.El
+.Pp
+The following capabilities are no longer supported by
+.Xr getty 8 :
+.Bl -column Name Type /usr/bin/login
+.It "bd num 0 backspace delay"
+.It "cb bool false use crt backspace mode"
+.It "cd num 0 carriage-return delay"
+.It "f0 num unused tty mode flags to write messages"
+.It "f1 num unused tty mode flags to read login name"
+.It "f2 num unused tty mode flags to leave terminal as"
+.It "fd num 0 form-feed (vertical motion) delay"
+.It "lc bool false terminal has lower case"
+.It "nd num 0 newline (line-feed) delay"
+.It "uc bool false terminal is known upper case only"
+.El
+.Pp
+If no line speed is specified, speed will not be altered
+from that which prevails when getty is entered.
+Specifying an input or output speed will override
+line speed for stated direction only.
+.Pp
+Terminal modes to be used for the output of the message,
+for input of the login name,
+and to leave the terminal set as upon completion,
+are derived from the boolean flags specified.
+If the derivation should prove inadequate,
+any (or all) of these three may be overridden
+with one of the
+.Va \&c0 ,
+.Va \&c1 ,
+.Va \&c2 ,
+.Va \&i0 ,
+.Va \&i1 ,
+.Va \&i2 ,
+.Va \&l0 ,
+.Va \&l1 ,
+.Va \&l2 ,
+.Va \&o0 ,
+.Va \&o1 ,
+or
+.Va \&o2
+numeric specifications, which can be used to specify
+(usually in octal, with a leading '0')
+the exact values of the flags.
+These flags correspond to the termios
+.Va c_cflag ,
+.Va c_iflag ,
+.Va c_lflag ,
+and
+.Va c_oflag
+fields, respectively.
+Each these sets must be completely specified to be effective.
+.Pp
+Should
+.Xr getty 8
+receive a null character
+(presumed to indicate a line break)
+it will restart using the table indicated by the
+.Va \&nx
+entry.
+If there is none, it will re-use its original table.
+.Pp
+Delays are specified in milliseconds, the nearest possible
+delay available in the tty driver will be used.
+Should greater certainty be desired, delays
+with values 0, 1, 2, and 3 are interpreted as
+choosing that particular delay algorithm from the driver.
+.Pp
+The
+.Va \&cl
+screen clear string may be preceded by a (decimal) number
+of milliseconds of delay required (a la termcap).
+This delay is simulated by repeated use of the pad character
+.Va \&pc .
+.Pp
+The initial message, login message, and initial file;
+.Va \&im ,
+.Va \&lm
+and
+.Va \&if
+may include any of the following character sequences, which expand to
+information about the environment in which
+.Xr getty 8
+is running.
+.Bl -tag -offset indent -width \&%xxxxxxxxxxxxxx
+.It \&%d
+The current date and time formatted according to the
+.Va \&Lo
+and
+.Va \&df
+strings.
+.It \&%h
+The hostname of the machine, which is normally obtained from the
+system using
+.Xr gethostname 3 ,
+but may also be overridden by the
+.Va \&hn
+table entry.
+In either case it may be edited with the
+.Va \&he
+POSIX
+.Dq extended
+regular expression, which is matched against the hostname.
+If there are no parenthesized subexpressions in the pattern,
+the entire matched string is used as the final hostname;
+otherwise, the first matched subexpression is used instead.
+If the pattern does not match, the original hostname is not modified.
+.It \&%t
+The tty name.
+.It "\&%m, \&%r, \&%s, \&%v"
+The type of machine, release of the operating system, name of the
+operating system, and version of the kernel, respectively, as
+returned by
+.Xr uname 3 .
+.It \&%%
+A
+.Dq %
+character.
+.El
+.Pp
+When getty execs the login process, given
+in the
+.Va \&lo
+string (usually
+.Dq Pa /usr/bin/login ) ,
+it will have set
+the environment to include the terminal type, as indicated
+by the
+.Va \&tt
+string (if it exists).
+The
+.Va \&ev
+string, can be used to enter additional data into
+the environment.
+It is a list of comma separated strings, each of which
+will presumably be of the form
+.Li name=value .
+.Pp
+If a non-zero timeout is specified, with
+.Va \&to ,
+then getty will exit within the indicated
+number of seconds, either having
+received a login name and passed control
+to
+.Xr login 1 ,
+or having received an alarm signal, and exited.
+This may be useful to hangup dial in lines.
+.Pp
+Output from
+.Xr getty 8
+is even parity unless
+.Va \&op
+or
+.Va \&np
+is specified.
+The
+.Va \&op
+string
+may be specified with
+.Va \&ap
+to allow any parity on input, but generate odd parity output.
+Note: this only applies while getty is being run,
+terminal driver limitations prevent a more complete
+implementation.
+The
+.Xr getty 8
+utility does not check parity of input characters in
+.Dv RAW
+mode.
+.Pp
+If a
+.Va \&pp
+string is specified and a PPP link bring-up sequence is recognized,
+getty will invoke the program referenced by the
+.Va \&pp
+option.
+This can be used to handle incoming PPP calls.
+If the
+.Va \&pl
+option is true as well,
+.Xr getty 8
+will skip the user name prompt and the PPP detection phase, and will
+invoke the program specified by
+.Va \&pp
+instantly.
+.Pp
+.Nm Getty
+provides some basic intelligent modem handling by providing a chat
+script feature available via two capabilities:
+.Pp
+.Bl -tag -offset indent -width \&xxxxxxxx -compact
+.It ic
+Chat script to initialize modem.
+.It ac
+Chat script to answer a call.
+.El
+.Pp
+A chat script is a set of expect/send string pairs.
+When a chat string starts,
+.Nm getty
+will wait for the first string, and if it finds it, will send the
+second, and so on.
+Strings specified are separated by one or more tabs or spaces.
+Strings may contain standard ASCII characters and special 'escapes',
+which consist of a backslash character followed by one or more
+characters which are interpreted as follows:
+.Pp
+.Bl -tag -offset indent -width \&xxxxxxxx -compact
+.It \ea
+bell character.
+.It \eb
+backspace.
+.It \en
+newline.
+.It \ee
+escape.
+.It \ef
+formfeed.
+.It \ep
+half-second pause.
+.It \er
+carriage return.
+.It \eS , \es
+space character.
+.It \et
+tab.
+.It \exNN
+hexadecimal byte value.
+.It \e0NNN
+octal byte value.
+.El
+.Pp
+Note that the
+.Ql \ep
+sequence is only valid for send strings and causes a half-second
+pause between sending the previous and next characters.
+Hexadecimal values are, at most, 2 hex digits long, and octal
+values are a maximum of 3 octal digits.
+.Pp
+The
+.Va \&ic
+chat sequence is used to initialize a modem or similar device.
+A typical example of an init chat script for a modem with a
+hayes compatible command set might look like this:
+.Pp
+.Dl :ic="" ATE0Q0V1\er OK\er ATS0=0\er OK\er:
+.Pp
+This script waits for nothing (which always succeeds), sends
+a sequence to ensure that the modem is in the correct mode
+(suppress command echo, send responses in verbose mode),
+and then disables auto-answer.
+It waits for an "OK" response before it terminates.
+The init sequence is used to check modem responses to ensure that
+the modem is functioning correctly.
+If the init script fails to complete,
+.Nm getty
+considers this to be fatal, and results in an error logged via
+.Xr syslogd 8 ,
+and exiting.
+.Pp
+Similarly, an answer chat script is used to manually answer the
+phone in response to (usually) a "RING".
+When run with an answer script,
+.Nm getty
+opens the port in non-blocking mode, clears any extraneous input
+and waits for data on the port.
+As soon as any data is available, the answer chat script is
+started and scanned for a string, and responds according to
+the answer chat script.
+With a hayes compatible modem, this would normally look something
+like:
+.Pp
+.Dl :ac=RING\er ATA\er CONNECT:
+.Pp
+This causes the modem to answer the call via the "ATA" command,
+then scans input for a "CONNECT" string.
+If this is received before a
+.Va \&ct
+timeout, then a normal login sequence commences.
+.Pp
+The
+.Va \&ct
+capability specifies a timeout for all send and expect strings.
+This timeout is set individually for each expect wait and send
+string and must be at least as long as the time it takes for
+a connection to be established between a remote and local
+modem (usually around 10 seconds).
+.Pp
+In most situations, you will want to flush any additional
+input after the connection has been detected, and the
+.Va \&de
+capability may be used to do that, as well as delay for a
+short time after the connection has been established during
+which all of the connection data has been sent by the modem.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr gethostname 3 ,
+.Xr uname 3 ,
+.Xr termcap 5 ,
+.Xr getty 8
+.Sh HISTORY
+The
+.Nm
+file format appeared in
+.Bx 4.2 .
+.Sh BUGS
+The special characters (erase, kill, etc.) are reset to system defaults
+by
+.Xr login 1 .
+In
+.Em all
+cases, '#' or '^H' typed in a login name will be treated as
+an erase character, and '@' will be treated as a kill character.
+.Pp
+The delay stuff is a real crock.
+Apart form its general lack of flexibility, some
+of the delay algorithms are not implemented.
+The terminal driver should support sane delay settings.
+.Pp
+The
+.Xr termcap 5
+format is horrid, something more rational should
+have been chosen.
diff --git a/libexec/getty/gettytab.h b/libexec/getty/gettytab.h
new file mode 100644
index 000000000000..d6b7384b7a44
--- /dev/null
+++ b/libexec/getty/gettytab.h
@@ -0,0 +1,173 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Getty description definitions.
+ */
+struct gettystrs {
+ const char *field; /* name to lookup in gettytab */
+ char *defalt; /* value we find by looking in defaults */
+ char *value; /* value that we find there */
+};
+
+struct gettynums {
+ const char *field; /* name to lookup */
+ long defalt; /* number we find in defaults */
+ long value; /* number we find there */
+ int set; /* we actually got this one */
+};
+
+struct gettyflags {
+ const char *field; /* name to lookup */
+ char invrt; /* name existing in gettytab --> false */
+ char defalt; /* true/false in defaults */
+ char value; /* true/false flag */
+ char set; /* we found it */
+};
+
+/*
+ * String values.
+ */
+#define NX gettystrs[0].value
+#define CL gettystrs[1].value
+#define IM gettystrs[2].value
+#define LM gettystrs[3].value
+#define ER gettystrs[4].value
+#define KL gettystrs[5].value
+#define ET gettystrs[6].value
+#define PC gettystrs[7].value
+#define TT gettystrs[8].value
+#define EV gettystrs[9].value
+#define LO gettystrs[10].value
+#define HN gettystrs[11].value
+#define HE gettystrs[12].value
+#define IN gettystrs[13].value
+#define QU gettystrs[14].value
+#define XN gettystrs[15].value
+#define XF gettystrs[16].value
+#define BK gettystrs[17].value
+#define SU gettystrs[18].value
+#define DS gettystrs[19].value
+#define RP gettystrs[20].value
+#define FL gettystrs[21].value
+#define WE gettystrs[22].value
+#define LN gettystrs[23].value
+#define Lo gettystrs[24].value
+#define PP gettystrs[25].value
+#define IF gettystrs[26].value
+#define IC gettystrs[27].value
+#define AC gettystrs[28].value
+#define AL gettystrs[29].value
+#define DF gettystrs[30].value
+#define IMP gettystrs[31].value
+
+/*
+ * Numeric definitions.
+ */
+#define IS gettynums[0].value
+#define OS gettynums[1].value
+#define SP gettynums[2].value
+#define ND gettynums[3].value
+#define CD gettynums[4].value
+#define TD gettynums[5].value
+#define FD gettynums[6].value
+#define BD gettynums[7].value
+#define TO gettynums[8].value
+#define F0 gettynums[9].value
+#define F0set gettynums[9].set
+#define F1 gettynums[10].value
+#define F1set gettynums[10].set
+#define F2 gettynums[11].value
+#define F2set gettynums[11].set
+#define PF gettynums[12].value
+#define C0 gettynums[13].value
+#define C0set gettynums[13].set
+#define C1 gettynums[14].value
+#define C1set gettynums[14].set
+#define C2 gettynums[15].value
+#define C2set gettynums[15].set
+#define I0 gettynums[16].value
+#define I0set gettynums[16].set
+#define I1 gettynums[17].value
+#define I1set gettynums[17].set
+#define I2 gettynums[18].value
+#define I2set gettynums[18].set
+#define L0 gettynums[19].value
+#define L0set gettynums[19].set
+#define L1 gettynums[20].value
+#define L1set gettynums[20].set
+#define L2 gettynums[21].value
+#define L2set gettynums[21].set
+#define O0 gettynums[22].value
+#define O0set gettynums[22].set
+#define O1 gettynums[23].value
+#define O1set gettynums[23].set
+#define O2 gettynums[24].value
+#define O2set gettynums[24].set
+#define DE gettynums[25].value
+#define RTset gettynums[26].set
+#define RT gettynums[26].value
+#define CT gettynums[27].value
+#define DC gettynums[28].value
+
+/*
+ * Boolean values.
+ */
+#define HT gettyflags[0].value
+#define NL gettyflags[1].value
+#define EP gettyflags[2].value
+#define EPset gettyflags[2].set
+#define OP gettyflags[3].value
+#define OPset gettyflags[3].set
+#define AP gettyflags[4].value
+#define APset gettyflags[4].set
+#define EC gettyflags[5].value
+#define CO gettyflags[6].value
+#define CB gettyflags[7].value
+#define CK gettyflags[8].value
+#define CE gettyflags[9].value
+#define PE gettyflags[10].value
+#define RW gettyflags[11].value
+#define XC gettyflags[12].value
+#define LC gettyflags[13].value
+#define UC gettyflags[14].value
+#define IG gettyflags[15].value
+#define PS gettyflags[16].value
+#define HC gettyflags[17].value
+#define UB gettyflags[18].value
+#define AB gettyflags[19].value
+#define DX gettyflags[20].value
+#define NP gettyflags[21].value
+#define NPset gettyflags[21].set
+#define MB gettyflags[22].value
+#define HW gettyflags[23].value
+#define NC gettyflags[24].value
+#define PL gettyflags[25].value
diff --git a/libexec/getty/init.c b/libexec/getty/init.c
new file mode 100644
index 000000000000..c304f86b568f
--- /dev/null
+++ b/libexec/getty/init.c
@@ -0,0 +1,148 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Getty table initializations.
+ *
+ * Melbourne getty.
+ */
+#include <stdio.h>
+#include <termios.h>
+#include "gettytab.h"
+#include "extern.h"
+#include "pathnames.h"
+
+static char loginmsg[] = "login: ";
+static char nullstr[] = "";
+static char loginprg[] = _PATH_LOGIN;
+static char datefmt[] = "%+";
+
+#define M(a) (char *)(&omode.c_cc[a])
+
+struct gettystrs gettystrs[] = {
+ { "nx", NULL, NULL }, /* next table */
+ { "cl", NULL, NULL }, /* screen clear characters */
+ { "im", NULL, NULL }, /* initial message */
+ { "lm", loginmsg, NULL }, /* login message */
+ { "er", M(VERASE), NULL }, /* erase character */
+ { "kl", M(VKILL), NULL }, /* kill character */
+ { "et", M(VEOF), NULL }, /* eof chatacter (eot) */
+ { "pc", nullstr, NULL }, /* pad character */
+ { "tt", NULL, NULL }, /* terminal type */
+ { "ev", NULL, NULL }, /* environment */
+ { "lo", loginprg, NULL }, /* login program */
+ { "hn", hostname, NULL }, /* host name */
+ { "he", NULL, NULL }, /* host name edit */
+ { "in", M(VINTR), NULL }, /* interrupt char */
+ { "qu", M(VQUIT), NULL }, /* quit char */
+ { "xn", M(VSTART), NULL }, /* XON (start) char */
+ { "xf", M(VSTOP), NULL }, /* XOFF (stop) char */
+ { "bk", M(VEOL), NULL }, /* brk char (alt \n) */
+ { "su", M(VSUSP), NULL }, /* suspend char */
+ { "ds", M(VDSUSP), NULL }, /* delayed suspend */
+ { "rp", M(VREPRINT), NULL }, /* reprint char */
+ { "fl", M(VDISCARD), NULL }, /* flush output */
+ { "we", M(VWERASE), NULL }, /* word erase */
+ { "ln", M(VLNEXT), NULL }, /* literal next */
+ { "Lo", NULL, NULL }, /* locale for strftime() */
+ { "pp", NULL, NULL }, /* ppp login program */
+ { "if", NULL, NULL }, /* sysv-like 'issue' filename */
+ { "ic", NULL, NULL }, /* modem init-chat */
+ { "ac", NULL, NULL }, /* modem answer-chat */
+ { "al", NULL, NULL }, /* user to auto-login */
+ { "df", datefmt, NULL }, /* format for strftime() */
+ { "iM" , NULL, NULL }, /* initial message program */
+ { NULL, NULL, NULL }
+};
+
+struct gettynums gettynums[] = {
+ { "is", 0, 0, 0 }, /* input speed */
+ { "os", 0, 0, 0 }, /* output speed */
+ { "sp", 0, 0, 0 }, /* both speeds */
+ { "nd", 0, 0, 0 }, /* newline delay */
+ { "cd", 0, 0, 0 }, /* carriage-return delay */
+ { "td", 0, 0, 0 }, /* tab delay */
+ { "fd", 0, 0, 0 }, /* form-feed delay */
+ { "bd", 0, 0, 0 }, /* backspace delay */
+ { "to", 0, 0, 0 }, /* timeout */
+ { "f0", 0, 0, 0 }, /* output flags */
+ { "f1", 0, 0, 0 }, /* input flags */
+ { "f2", 0, 0, 0 }, /* user mode flags */
+ { "pf", 0, 0, 0 }, /* delay before flush at 1st prompt */
+ { "c0", 0, 0, 0 }, /* output c_flags */
+ { "c1", 0, 0, 0 }, /* input c_flags */
+ { "c2", 0, 0, 0 }, /* user mode c_flags */
+ { "i0", 0, 0, 0 }, /* output i_flags */
+ { "i1", 0, 0, 0 }, /* input i_flags */
+ { "i2", 0, 0, 0 }, /* user mode i_flags */
+ { "l0", 0, 0, 0 }, /* output l_flags */
+ { "l1", 0, 0, 0 }, /* input l_flags */
+ { "l2", 0, 0, 0 }, /* user mode l_flags */
+ { "o0", 0, 0, 0 }, /* output o_flags */
+ { "o1", 0, 0, 0 }, /* input o_flags */
+ { "o2", 0, 0, 0 }, /* user mode o_flags */
+ { "de", 0, 0, 0 }, /* delay before sending 1st prompt */
+ { "rt", 0, 0, 0 }, /* reset timeout */
+ { "ct", 0, 0, 0 }, /* chat script timeout */
+ { "dc", 0, 0, 0 }, /* debug chat script value */
+ { NULL, 0, 0, 0 }
+};
+
+
+struct gettyflags gettyflags[] = {
+ { "ht", 0, 0, 0, 0 }, /* has tabs */
+ { "nl", 1, 0, 0, 0 }, /* has newline char */
+ { "ep", 0, 0, 0, 0 }, /* even parity */
+ { "op", 0, 0, 0, 0 }, /* odd parity */
+ { "ap", 0, 0, 0, 0 }, /* any parity */
+ { "ec", 1, 0, 0, 0 }, /* no echo */
+ { "co", 0, 0, 0, 0 }, /* console special */
+ { "cb", 0, 0, 0, 0 }, /* crt backspace */
+ { "ck", 0, 0, 0, 0 }, /* crt kill */
+ { "ce", 0, 0, 0, 0 }, /* crt erase */
+ { "pe", 0, 0, 0, 0 }, /* printer erase */
+ { "rw", 1, 0, 0, 0 }, /* don't use raw */
+ { "xc", 1, 0, 0, 0 }, /* don't ^X ctl chars */
+ { "lc", 0, 0, 0, 0 }, /* terminal las lower case */
+ { "uc", 0, 0, 0, 0 }, /* terminal has no lower case */
+ { "ig", 0, 0, 0, 0 }, /* ignore garbage */
+ { "ps", 0, 0, 0, 0 }, /* do port selector speed select */
+ { "hc", 1, 0, 0, 0 }, /* don't set hangup on close */
+ { "ub", 0, 0, 0, 0 }, /* unbuffered output */
+ { "ab", 0, 0, 0, 0 }, /* auto-baud detect with '\r' */
+ { "dx", 0, 0, 0, 0 }, /* set decctlq */
+ { "np", 0, 0, 0, 0 }, /* no parity at all (8bit chars) */
+ { "mb", 0, 0, 0, 0 }, /* do MDMBUF flow control */
+ { "hw", 0, 0, 0, 0 }, /* do CTSRTS flow control */
+ { "nc", 0, 0, 0, 0 }, /* set clocal (no carrier) */
+ { "pl", 0, 0, 0, 0 }, /* use PPP instead of login(1) */
+ { NULL, 0, 0, 0, 0 }
+};
diff --git a/libexec/getty/main.c b/libexec/getty/main.c
new file mode 100644
index 000000000000..0e8a08f81b5f
--- /dev/null
+++ b/libexec/getty/main.c
@@ -0,0 +1,806 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/ttydefaults.h>
+#include <sys/utsname.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <libutil.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "gettytab.h"
+#include "extern.h"
+#include "pathnames.h"
+
+/*
+ * Set the amount of running time that getty should accumulate
+ * before deciding that something is wrong and exit.
+ */
+#define GETTY_TIMEOUT 60 /* seconds */
+
+#undef CTRL
+#define CTRL(x) (x&037)
+
+/* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
+
+#define PPP_FRAME 0x7e /* PPP Framing character */
+#define PPP_STATION 0xff /* "All Station" character */
+#define PPP_ESCAPE 0x7d /* Escape Character */
+#define PPP_CONTROL 0x03 /* PPP Control Field */
+#define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */
+#define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */
+#define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */
+
+/* original mode; flags've been reset using values from <sys/ttydefaults.h> */
+struct termios omode;
+/* current mode */
+struct termios tmode;
+
+static int crmod, digit, lower, upper;
+
+char hostname[MAXHOSTNAMELEN];
+static char name[MAXLOGNAME*3];
+static char ttyn[32];
+
+#define OBUFSIZ 128
+
+static const char *tname;
+
+static char *env[128];
+
+static char partab[] = {
+ 0001,0201,0201,0001,0201,0001,0001,0201,
+ 0202,0004,0003,0205,0005,0206,0201,0001,
+ 0201,0001,0001,0201,0001,0201,0201,0001,
+ 0001,0201,0201,0001,0201,0001,0001,0201,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0201
+};
+
+#define ERASE tmode.c_cc[VERASE]
+#define KILL tmode.c_cc[VKILL]
+#define EOT tmode.c_cc[VEOF]
+
+#define puts Gputs
+
+static void defttymode(void);
+static void dingdong(int);
+static void dogettytab(void);
+static int getname(void);
+static void interrupt(int);
+static void oflush(void);
+static void prompt(void);
+static void putchr(int);
+static void putf(const char *);
+static void putpad(const char *);
+static void puts(const char *);
+static void timeoverrun(int);
+static char *get_line(int);
+static void setttymode(int);
+static int opentty(const char *, int);
+
+static jmp_buf timeout;
+
+static void
+dingdong(int signo __unused)
+{
+ alarm(0);
+ longjmp(timeout, 1);
+}
+
+static jmp_buf intrupt;
+
+static void
+interrupt(int signo __unused)
+{
+ longjmp(intrupt, 1);
+}
+
+/*
+ * Action to take when getty is running too long.
+ */
+static void
+timeoverrun(int signo __unused)
+{
+
+ syslog(LOG_ERR, "getty exiting due to excessive running time");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int first_sleep = 1, first_time = 1;
+ struct rlimit limit;
+ int rval;
+
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+
+ openlog("getty", LOG_CONS|LOG_PID, LOG_AUTH);
+ gethostname(hostname, sizeof(hostname) - 1);
+ hostname[sizeof(hostname) - 1] = '\0';
+ if (hostname[0] == '\0')
+ snprintf(hostname, sizeof(hostname), "Amnesiac");
+
+ /*
+ * Limit running time to deal with broken or dead lines.
+ */
+ (void)signal(SIGXCPU, timeoverrun);
+ limit.rlim_max = RLIM_INFINITY;
+ limit.rlim_cur = GETTY_TIMEOUT;
+ (void)setrlimit(RLIMIT_CPU, &limit);
+
+ gettable("default");
+ gendefaults();
+ tname = "default";
+ if (argc > 1)
+ tname = argv[1];
+
+ /*
+ * The following is a work around for vhangup interactions
+ * which cause great problems getting window systems started.
+ * If the tty line is "-", we do the old style getty presuming
+ * that the file descriptors are already set up for us.
+ * J. Gettys - MIT Project Athena.
+ */
+ if (argc <= 2 || strcmp(argv[2], "-") == 0) {
+ char *n = ttyname(STDIN_FILENO);
+ if (n == NULL) {
+ syslog(LOG_ERR, "ttyname: %m");
+ exit(1);
+ }
+ snprintf(ttyn, sizeof(ttyn), "%s", n);
+ } else {
+ snprintf(ttyn, sizeof(ttyn), "%s%s", _PATH_DEV, argv[2]);
+ if (strcmp(argv[0], "+") != 0) {
+ chown(ttyn, 0, 0);
+ chmod(ttyn, 0600);
+ revoke(ttyn);
+
+ /*
+ * Do the first scan through gettytab.
+ * Terminal mode parameters will be wrong until
+ * defttymode() called, but they're irrelevant for
+ * the initial setup of the terminal device.
+ */
+ dogettytab();
+
+ /*
+ * Init or answer modem sequence has been specified.
+ */
+ if (IC || AC) {
+ if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
+ exit(1);
+ defttymode();
+ setttymode(1);
+ }
+
+ if (IC) {
+ if (getty_chat(IC, CT, DC) > 0) {
+ syslog(LOG_ERR, "modem init problem on %s", ttyn);
+ (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
+ exit(1);
+ }
+ }
+
+ if (AC) {
+ fd_set rfds;
+ struct timeval to;
+ int i;
+
+ FD_ZERO(&rfds);
+ FD_SET(0, &rfds);
+ to.tv_sec = RT;
+ to.tv_usec = 0;
+ i = select(32, &rfds, NULL, NULL, RT ? &to : NULL);
+ if (i < 0) {
+ syslog(LOG_ERR, "select %s: %m", ttyn);
+ } else if (i == 0) {
+ syslog(LOG_NOTICE, "recycle tty %s", ttyn);
+ (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
+ exit(0); /* recycle for init */
+ }
+ i = getty_chat(AC, CT, DC);
+ if (i > 0) {
+ syslog(LOG_ERR, "modem answer problem on %s", ttyn);
+ (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
+ exit(1);
+ }
+ } else { /* maybe blocking open */
+ if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
+ exit(1);
+ }
+ }
+ }
+
+ defttymode();
+ for (;;) {
+
+ /*
+ * if a delay was specified then sleep for that
+ * number of seconds before writing the initial prompt
+ */
+ if (first_sleep && DE) {
+ sleep(DE);
+ /* remove any noise */
+ (void)tcflush(STDIN_FILENO, TCIOFLUSH);
+ }
+ first_sleep = 0;
+
+ setttymode(0);
+ if (AB) {
+ tname = autobaud();
+ dogettytab();
+ continue;
+ }
+ if (PS) {
+ tname = portselector();
+ dogettytab();
+ continue;
+ }
+ if (CL && *CL)
+ putpad(CL);
+ edithost(HE);
+
+ /* if this is the first time through this, and an
+ issue file has been given, then send it */
+ if (first_time && IF) {
+ int fd;
+
+ if ((fd = open(IF, O_RDONLY)) != -1) {
+ char * cp;
+
+ while ((cp = get_line(fd)) != NULL) {
+ putf(cp);
+ }
+ close(fd);
+ }
+ }
+ first_time = 0;
+
+ if (IMP && *IMP && !(PL && PP))
+ system(IMP);
+ if (IM && *IM && !(PL && PP))
+ putf(IM);
+ if (setjmp(timeout)) {
+ cfsetispeed(&tmode, B0);
+ cfsetospeed(&tmode, B0);
+ (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
+ exit(1);
+ }
+ if (TO) {
+ signal(SIGALRM, dingdong);
+ alarm(TO);
+ }
+
+ rval = 0;
+ if (AL) {
+ const char *p = AL;
+ char *q = name;
+
+ while (*p && q < &name[sizeof name - 1]) {
+ if (isupper(*p))
+ upper = 1;
+ else if (islower(*p))
+ lower = 1;
+ else if (isdigit(*p))
+ digit = 1;
+ *q++ = *p++;
+ }
+ } else if (!(PL && PP))
+ rval = getname();
+ if (rval == 2 || (PL && PP)) {
+ oflush();
+ alarm(0);
+ limit.rlim_max = RLIM_INFINITY;
+ limit.rlim_cur = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &limit);
+ execle(PP, "ppplogin", ttyn, (char *) 0, env);
+ syslog(LOG_ERR, "%s: %m", PP);
+ exit(1);
+ } else if (rval || AL) {
+ int i;
+
+ oflush();
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ if (name[0] == '\0')
+ continue;
+ if (name[0] == '-') {
+ puts("user names may not start with '-'.");
+ continue;
+ }
+ if (!(upper || lower || digit)) {
+ if (AL) {
+ syslog(LOG_ERR,
+ "invalid auto-login name: %s", AL);
+ exit(1);
+ } else
+ continue;
+ }
+ set_flags(2);
+ if (crmod) {
+ tmode.c_iflag |= ICRNL;
+ tmode.c_oflag |= ONLCR;
+ }
+#if REALLY_OLD_TTYS
+ if (upper || UC)
+ tmode.sg_flags |= LCASE;
+ if (lower || LC)
+ tmode.sg_flags &= ~LCASE;
+#endif
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
+ syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
+ exit(1);
+ }
+ signal(SIGINT, SIG_DFL);
+ for (i = 0; environ[i] != (char *)0; i++)
+ env[i] = environ[i];
+ makeenv(&env[i]);
+
+ limit.rlim_max = RLIM_INFINITY;
+ limit.rlim_cur = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &limit);
+ execle(LO, "login", AL ? "-fp" : "-p", name,
+ (char *) 0, env);
+ syslog(LOG_ERR, "%s: %m", LO);
+ exit(1);
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGINT, SIG_IGN);
+ if (NX && *NX) {
+ tname = NX;
+ dogettytab();
+ }
+ }
+}
+
+static int
+opentty(const char *tty, int flags)
+{
+ int failopenlogged = 0, i, saved_errno;
+
+ while ((i = open(tty, flags)) == -1)
+ {
+ saved_errno = errno;
+ if (!failopenlogged) {
+ syslog(LOG_ERR, "open %s: %m", tty);
+ failopenlogged = 1;
+ }
+ if (saved_errno == ENOENT)
+ return 0;
+ sleep(60);
+ }
+ if (login_tty(i) < 0) {
+ if (daemon(0,0) < 0) {
+ syslog(LOG_ERR,"daemon: %m");
+ close(i);
+ return 0;
+ }
+ if (login_tty(i) < 0) {
+ syslog(LOG_ERR, "login_tty %s: %m", tty);
+ close(i);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void
+defttymode(void)
+{
+ struct termios def;
+
+ /* Start with default tty settings. */
+ if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
+ syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
+ exit(1);
+ }
+ omode = tmode; /* fill c_cc for dogettytab() */
+ dogettytab();
+ /*
+ * Don't rely on the driver too much, and initialize crucial
+ * things according to <sys/ttydefaults.h>. Avoid clobbering
+ * the c_cc[] settings however, the console drivers might wish
+ * to leave their idea of the preferred VERASE key value
+ * there.
+ */
+ cfmakesane(&def);
+ tmode.c_iflag = def.c_iflag;
+ tmode.c_oflag = def.c_oflag;
+ tmode.c_lflag = def.c_lflag;
+ tmode.c_cflag = def.c_cflag;
+ if (NC)
+ tmode.c_cflag |= CLOCAL;
+ omode = tmode;
+}
+
+static void
+setttymode(int raw)
+{
+ int off = 0;
+
+ (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
+ ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */
+ ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */
+
+ if (IS)
+ cfsetispeed(&tmode, speed(IS));
+ else if (SP)
+ cfsetispeed(&tmode, speed(SP));
+ if (OS)
+ cfsetospeed(&tmode, speed(OS));
+ else if (SP)
+ cfsetospeed(&tmode, speed(SP));
+ set_flags(0);
+ setchars();
+ if (raw)
+ cfmakeraw(&tmode);
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
+ syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
+ exit(1);
+ }
+}
+
+
+static int
+getname(void)
+{
+ int c;
+ char *np;
+ unsigned char cs;
+ int ppp_state = 0;
+ int ppp_connection = 0;
+
+ /*
+ * Interrupt may happen if we use CBREAK mode
+ */
+ if (setjmp(intrupt)) {
+ signal(SIGINT, SIG_IGN);
+ return (0);
+ }
+ signal(SIGINT, interrupt);
+ set_flags(1);
+ prompt();
+ oflush();
+ if (PF > 0) {
+ sleep(PF);
+ PF = 0;
+ }
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
+ syslog(LOG_ERR, "%s: %m", ttyn);
+ exit(1);
+ }
+ crmod = digit = lower = upper = 0;
+ np = name;
+ for (;;) {
+ oflush();
+ if (read(STDIN_FILENO, &cs, 1) <= 0)
+ exit(0);
+ if ((c = cs&0177) == 0)
+ return (0);
+
+ /* PPP detection state machine..
+ Look for sequences:
+ PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
+ PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
+ See RFC1662.
+ Derived from code from Michael Hancock, <michaelh@cet.co.jp>
+ and Erik 'PPP' Olson, <eriko@wrq.com>
+ */
+
+ if (PP && (cs == PPP_FRAME)) {
+ ppp_state = 1;
+ } else if (ppp_state == 1 && cs == PPP_STATION) {
+ ppp_state = 2;
+ } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
+ ppp_state = 3;
+ } else if ((ppp_state == 2 && cs == PPP_CONTROL)
+ || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
+ ppp_state = 4;
+ } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
+ ppp_state = 5;
+ } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
+ ppp_connection = 1;
+ break;
+ } else {
+ ppp_state = 0;
+ }
+
+ if (c == EOT || c == CTRL('d'))
+ exit(0);
+ if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
+ putf("\r\n");
+ break;
+ }
+ if (islower(c))
+ lower = 1;
+ else if (isupper(c))
+ upper = 1;
+ else if (c == ERASE || c == '\b' || c == 0177) {
+ if (np > name) {
+ np--;
+ if (cfgetospeed(&tmode) >= 1200)
+ puts("\b \b");
+ else
+ putchr(cs);
+ }
+ continue;
+ } else if (c == KILL || c == CTRL('u')) {
+ putchr('\r');
+ if (cfgetospeed(&tmode) < 1200)
+ putchr('\n');
+ /* this is the way they do it down under ... */
+ else if (np > name)
+ puts(" \r");
+ prompt();
+ digit = lower = upper = 0;
+ np = name;
+ continue;
+ } else if (isdigit(c))
+ digit = 1;
+ if (IG && (c <= ' ' || c > 0176))
+ continue;
+ *np++ = c;
+ putchr(cs);
+ }
+ signal(SIGINT, SIG_IGN);
+ *np = 0;
+ if (c == '\r')
+ crmod = 1;
+ if ((upper && !lower && !LC) || UC)
+ for (np = name; *np; np++)
+ if (isupper(*np))
+ *np = tolower(*np);
+ return (1 + ppp_connection);
+}
+
+static void
+putpad(const char *s)
+{
+ int pad = 0;
+ speed_t ospeed = cfgetospeed(&tmode);
+
+ if (isdigit(*s)) {
+ while (isdigit(*s)) {
+ pad *= 10;
+ pad += *s++ - '0';
+ }
+ pad *= 10;
+ if (*s == '.' && isdigit(s[1])) {
+ pad += s[1] - '0';
+ s += 2;
+ }
+ }
+
+ puts(s);
+ /*
+ * If no delay needed, or output speed is
+ * not comprehensible, then don't try to delay.
+ */
+ if (pad == 0 || ospeed <= 0)
+ return;
+
+ /*
+ * Round up by a half a character frame, and then do the delay.
+ * Too bad there are no user program accessible programmed delays.
+ * Transmitting pad characters slows many terminals down and also
+ * loads the system.
+ */
+ pad = (pad * ospeed + 50000) / 100000;
+ while (pad--)
+ putchr(*PC);
+}
+
+static void
+puts(const char *s)
+{
+ while (*s)
+ putchr(*s++);
+}
+
+static char outbuf[OBUFSIZ];
+static int obufcnt = 0;
+
+static void
+putchr(int cc)
+{
+ char c;
+
+ c = cc;
+ if (!NP) {
+ c |= partab[c&0177] & 0200;
+ if (OP)
+ c ^= 0200;
+ }
+ if (!UB) {
+ outbuf[obufcnt++] = c;
+ if (obufcnt >= OBUFSIZ)
+ oflush();
+ } else
+ write(STDOUT_FILENO, &c, 1);
+}
+
+static void
+oflush(void)
+{
+ if (obufcnt)
+ write(STDOUT_FILENO, outbuf, obufcnt);
+ obufcnt = 0;
+}
+
+static void
+prompt(void)
+{
+
+ putf(LM);
+ if (CO)
+ putchr('\n');
+}
+
+
+static char *
+get_line(int fd)
+{
+ size_t i = 0;
+ static char linebuf[512];
+
+ /*
+ * This is certainly slow, but it avoids having to include
+ * stdio.h unnecessarily. Issue files should be small anyway.
+ */
+ while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
+ if (linebuf[i] == '\n') {
+ /* Don't rely on newline mode, assume raw */
+ linebuf[i++] = '\r';
+ linebuf[i++] = '\n';
+ linebuf[i] = '\0';
+ return linebuf;
+ }
+ ++i;
+ }
+ linebuf[i] = '\0';
+ return i ? linebuf : 0;
+}
+
+static void
+putf(const char *cp)
+{
+ time_t t;
+ char db[100];
+ const char *slash;
+
+ static struct utsname kerninfo;
+
+ if (!*kerninfo.sysname)
+ uname(&kerninfo);
+
+ while (*cp) {
+ if (*cp != '%') {
+ putchr(*cp++);
+ continue;
+ }
+ switch (*++cp) {
+
+ case 't':
+ slash = strrchr(ttyn, '/');
+ if (slash == (char *) 0)
+ puts(ttyn);
+ else
+ puts(&slash[1]);
+ break;
+
+ case 'h':
+ puts(editedhost);
+ break;
+
+ case 'd':
+ t = (time_t)0;
+ (void)time(&t);
+ if (Lo)
+ (void)setlocale(LC_TIME, Lo);
+ (void)strftime(db, sizeof(db), DF, localtime(&t));
+ puts(db);
+ break;
+
+ case 's':
+ puts(kerninfo.sysname);
+ break;
+
+ case 'm':
+ puts(kerninfo.machine);
+ break;
+
+ case 'r':
+ puts(kerninfo.release);
+ break;
+
+ case 'v':
+ puts(kerninfo.version);
+ break;
+
+ case '%':
+ putchr('%');
+ break;
+ }
+ cp++;
+ }
+}
+
+/*
+ * Read a gettytab database entry and perform necessary quirks.
+ */
+static void
+dogettytab(void)
+{
+
+ /* Read the database entry. */
+ gettable(tname);
+
+ /*
+ * Avoid inheriting the parity values from the default entry
+ * if any of them is set in the current entry.
+ * Mixing different parity settings is unreasonable.
+ */
+ if (OPset || EPset || APset || NPset)
+ OPset = EPset = APset = NPset = 1;
+
+ /* Fill in default values for unset capabilities. */
+ setdefaults();
+}
diff --git a/libexec/getty/pathnames.h b/libexec/getty/pathnames.h
new file mode 100644
index 000000000000..1de5551a196a
--- /dev/null
+++ b/libexec/getty/pathnames.h
@@ -0,0 +1,35 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <paths.h>
+
+#define _PATH_GETTYTAB "/etc/gettytab"
+#define _PATH_LOGIN "/usr/bin/login"
diff --git a/libexec/getty/subr.c b/libexec/getty/subr.c
new file mode 100644
index 000000000000..05186f593bf4
--- /dev/null
+++ b/libexec/getty/subr.c
@@ -0,0 +1,676 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Melbourne getty.
+ */
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <poll.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "gettytab.h"
+#include "pathnames.h"
+#include "extern.h"
+
+/*
+ * Get a table entry.
+ */
+void
+gettable(const char *name)
+{
+ char *buf = NULL;
+ struct gettystrs *sp;
+ struct gettynums *np;
+ struct gettyflags *fp;
+ long n;
+ int l;
+ char *p;
+ static char path_gettytab[PATH_MAX];
+ char *dba[2];
+
+ static int firsttime = 1;
+
+ strlcpy(path_gettytab, _PATH_GETTYTAB, sizeof(path_gettytab));
+ dba[0] = path_gettytab;
+ dba[1] = NULL;
+
+ if (firsttime) {
+ /*
+ * we need to strdup() anything in the strings array
+ * initially in order to simplify things later
+ */
+ for (sp = gettystrs; sp->field; sp++)
+ if (sp->value != NULL) {
+ /* handle these ones more carefully */
+ if (sp >= &gettystrs[4] && sp <= &gettystrs[6])
+ l = 2;
+ else
+ l = strlen(sp->value) + 1;
+ if ((p = malloc(l)) != NULL)
+ strlcpy(p, sp->value, l);
+ /*
+ * replace, even if NULL, else we'll
+ * have problems with free()ing static mem
+ */
+ sp->value = p;
+ }
+ firsttime = 0;
+ }
+
+ switch (cgetent(&buf, dba, name)) {
+ case 1:
+ syslog(LOG_ERR, "getty: couldn't resolve 'tc=' in gettytab '%s'", name);
+ return;
+ case 0:
+ break;
+ case -1:
+ syslog(LOG_ERR, "getty: unknown gettytab entry '%s'", name);
+ return;
+ case -2:
+ syslog(LOG_ERR, "getty: retrieving gettytab entry '%s': %m", name);
+ return;
+ case -3:
+ syslog(LOG_ERR, "getty: recursive 'tc=' reference gettytab entry '%s'", name);
+ return;
+ default:
+ syslog(LOG_ERR, "getty: unexpected cgetent() error for entry '%s'", name);
+ return;
+ }
+
+ for (sp = gettystrs; sp->field; sp++) {
+ if ((l = cgetstr(buf, sp->field, &p)) >= 0) {
+ if (sp->value) {
+ /* prefer existing value */
+ if (strcmp(p, sp->value) != 0)
+ free(sp->value);
+ else {
+ free(p);
+ p = sp->value;
+ }
+ }
+ sp->value = p;
+ } else if (l == -1) {
+ free(sp->value);
+ sp->value = NULL;
+ }
+ }
+
+ for (np = gettynums; np->field; np++) {
+ if (cgetnum(buf, np->field, &n) == -1)
+ np->set = 0;
+ else {
+ np->set = 1;
+ np->value = n;
+ }
+ }
+
+ for (fp = gettyflags; fp->field; fp++) {
+ if (cgetcap(buf, fp->field, ':') == NULL)
+ fp->set = 0;
+ else {
+ fp->set = 1;
+ fp->value = 1 ^ fp->invrt;
+ }
+ }
+ free(buf);
+}
+
+void
+gendefaults(void)
+{
+ struct gettystrs *sp;
+ struct gettynums *np;
+ struct gettyflags *fp;
+
+ for (sp = gettystrs; sp->field; sp++)
+ if (sp->value)
+ sp->defalt = strdup(sp->value);
+ for (np = gettynums; np->field; np++)
+ if (np->set)
+ np->defalt = np->value;
+ for (fp = gettyflags; fp->field; fp++)
+ if (fp->set)
+ fp->defalt = fp->value;
+ else
+ fp->defalt = fp->invrt;
+}
+
+void
+setdefaults(void)
+{
+ struct gettystrs *sp;
+ struct gettynums *np;
+ struct gettyflags *fp;
+
+ for (sp = gettystrs; sp->field; sp++)
+ if (!sp->value)
+ sp->value = !sp->defalt ?
+ sp->defalt : strdup(sp->defalt);
+ for (np = gettynums; np->field; np++)
+ if (!np->set)
+ np->value = np->defalt;
+ for (fp = gettyflags; fp->field; fp++)
+ if (!fp->set)
+ fp->value = fp->defalt;
+}
+
+static char **
+charnames[] = {
+ &ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK,
+ &SU, &DS, &RP, &FL, &WE, &LN, 0
+};
+
+#define CV(a) (char *)(&tmode.c_cc[a])
+
+static char *
+charvars[] = {
+ CV(VERASE), CV(VKILL), CV(VINTR),
+ CV(VQUIT), CV(VSTART), CV(VSTOP),
+ CV(VEOF), CV(VEOL), CV(VSUSP),
+ CV(VDSUSP), CV(VREPRINT), CV(VDISCARD),
+ CV(VWERASE), CV(VLNEXT), 0
+};
+
+void
+setchars(void)
+{
+ int i;
+ const char *p;
+
+ for (i = 0; charnames[i]; i++) {
+ p = *charnames[i];
+ if (p && *p)
+ *charvars[i] = *p;
+ else
+ *charvars[i] = _POSIX_VDISABLE;
+ }
+}
+
+/* Macros to clear/set/test flags. */
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~(f)
+#define ISSET(t, f) ((t) & (f))
+
+void
+set_flags(int n)
+{
+ tcflag_t iflag, oflag, cflag, lflag;
+
+
+ switch (n) {
+ case 0:
+ if (C0set && I0set && L0set && O0set) {
+ tmode.c_cflag = C0;
+ tmode.c_iflag = I0;
+ tmode.c_lflag = L0;
+ tmode.c_oflag = O0;
+ return;
+ }
+ break;
+ case 1:
+ if (C1set && I1set && L1set && O1set) {
+ tmode.c_cflag = C1;
+ tmode.c_iflag = I1;
+ tmode.c_lflag = L1;
+ tmode.c_oflag = O1;
+ return;
+ }
+ break;
+ default:
+ if (C2set && I2set && L2set && O2set) {
+ tmode.c_cflag = C2;
+ tmode.c_iflag = I2;
+ tmode.c_lflag = L2;
+ tmode.c_oflag = O2;
+ return;
+ }
+ break;
+ }
+
+ iflag = omode.c_iflag;
+ oflag = omode.c_oflag;
+ cflag = omode.c_cflag;
+ lflag = omode.c_lflag;
+
+ if (NP) {
+ CLR(cflag, CSIZE|PARENB);
+ SET(cflag, CS8);
+ CLR(iflag, ISTRIP|INPCK|IGNPAR);
+ } else if (AP || EP || OP) {
+ CLR(cflag, CSIZE);
+ SET(cflag, CS7|PARENB);
+ SET(iflag, ISTRIP);
+ if (OP && !EP) {
+ SET(iflag, INPCK|IGNPAR);
+ SET(cflag, PARODD);
+ if (AP)
+ CLR(iflag, INPCK);
+ } else if (EP && !OP) {
+ SET(iflag, INPCK|IGNPAR);
+ CLR(cflag, PARODD);
+ if (AP)
+ CLR(iflag, INPCK);
+ } else if (AP || (EP && OP)) {
+ CLR(iflag, INPCK|IGNPAR);
+ CLR(cflag, PARODD);
+ }
+ } /* else, leave as is */
+
+#if 0
+ if (UC)
+ f |= LCASE;
+#endif
+
+ if (HC)
+ SET(cflag, HUPCL);
+ else
+ CLR(cflag, HUPCL);
+
+ if (MB)
+ SET(cflag, MDMBUF);
+ else
+ CLR(cflag, MDMBUF);
+
+ if (HW)
+ SET(cflag, CRTSCTS);
+ else
+ CLR(cflag, CRTSCTS);
+
+ if (NL) {
+ SET(iflag, ICRNL);
+ SET(oflag, ONLCR|OPOST);
+ } else {
+ CLR(iflag, ICRNL);
+ CLR(oflag, ONLCR);
+ }
+
+ if (!HT)
+ SET(oflag, OXTABS|OPOST);
+ else
+ CLR(oflag, OXTABS);
+
+#ifdef XXX_DELAY
+ SET(f, delaybits());
+#endif
+
+ if (n == 1) { /* read mode flags */
+ if (RW) {
+ iflag = 0;
+ CLR(oflag, OPOST);
+ CLR(cflag, CSIZE|PARENB);
+ SET(cflag, CS8);
+ lflag = 0;
+ } else {
+ CLR(lflag, ICANON);
+ }
+ goto out;
+ }
+
+ if (n == 0)
+ goto out;
+
+#if 0
+ if (CB)
+ SET(f, CRTBS);
+#endif
+
+ if (CE)
+ SET(lflag, ECHOE);
+ else
+ CLR(lflag, ECHOE);
+
+ if (CK)
+ SET(lflag, ECHOKE);
+ else
+ CLR(lflag, ECHOKE);
+
+ if (PE)
+ SET(lflag, ECHOPRT);
+ else
+ CLR(lflag, ECHOPRT);
+
+ if (EC)
+ SET(lflag, ECHO);
+ else
+ CLR(lflag, ECHO);
+
+ if (XC)
+ SET(lflag, ECHOCTL);
+ else
+ CLR(lflag, ECHOCTL);
+
+ if (DX)
+ SET(lflag, IXANY);
+ else
+ CLR(lflag, IXANY);
+
+out:
+ tmode.c_iflag = iflag;
+ tmode.c_oflag = oflag;
+ tmode.c_cflag = cflag;
+ tmode.c_lflag = lflag;
+}
+
+
+#ifdef XXX_DELAY
+struct delayval {
+ unsigned delay; /* delay in ms */
+ int bits;
+};
+
+/*
+ * below are random guesses, I can't be bothered checking
+ */
+
+struct delayval crdelay[] = {
+ { 1, CR1 },
+ { 2, CR2 },
+ { 3, CR3 },
+ { 83, CR1 },
+ { 166, CR2 },
+ { 0, CR3 },
+};
+
+struct delayval nldelay[] = {
+ { 1, NL1 }, /* special, calculated */
+ { 2, NL2 },
+ { 3, NL3 },
+ { 100, NL2 },
+ { 0, NL3 },
+};
+
+struct delayval bsdelay[] = {
+ { 1, BS1 },
+ { 0, 0 },
+};
+
+struct delayval ffdelay[] = {
+ { 1, FF1 },
+ { 1750, FF1 },
+ { 0, FF1 },
+};
+
+struct delayval tbdelay[] = {
+ { 1, TAB1 },
+ { 2, TAB2 },
+ { 3, XTABS }, /* this is expand tabs */
+ { 100, TAB1 },
+ { 0, TAB2 },
+};
+
+int
+delaybits(void)
+{
+ int f;
+
+ f = adelay(CD, crdelay);
+ f |= adelay(ND, nldelay);
+ f |= adelay(FD, ffdelay);
+ f |= adelay(TD, tbdelay);
+ f |= adelay(BD, bsdelay);
+ return (f);
+}
+
+int
+adelay(int ms, struct delayval *dp)
+{
+ if (ms == 0)
+ return (0);
+ while (dp->delay && ms > dp->delay)
+ dp++;
+ return (dp->bits);
+}
+#endif
+
+char editedhost[MAXHOSTNAMELEN];
+
+void
+edithost(const char *pattern)
+{
+ regex_t regex;
+ regmatch_t *match;
+ int found;
+
+ if (pattern == NULL || *pattern == '\0')
+ goto copyasis;
+ if (regcomp(&regex, pattern, REG_EXTENDED) != 0)
+ goto copyasis;
+
+ match = calloc(regex.re_nsub + 1, sizeof(*match));
+ if (match == NULL) {
+ regfree(&regex);
+ goto copyasis;
+ }
+
+ found = !regexec(&regex, HN, regex.re_nsub + 1, match, 0);
+ if (found) {
+ size_t subex, totalsize;
+
+ /*
+ * We found a match. If there were no parenthesized
+ * subexpressions in the pattern, use entire matched
+ * string as ``editedhost''; otherwise use the first
+ * matched subexpression.
+ */
+ subex = !!regex.re_nsub;
+ totalsize = match[subex].rm_eo - match[subex].rm_so + 1;
+ strlcpy(editedhost, HN + match[subex].rm_so, totalsize >
+ sizeof(editedhost) ? sizeof(editedhost) : totalsize);
+ }
+ free(match);
+ regfree(&regex);
+ if (found)
+ return;
+ /*
+ * In case of any errors, or if the pattern did not match, pass
+ * the original hostname as is.
+ */
+copyasis:
+ strlcpy(editedhost, HN, sizeof(editedhost));
+}
+
+static struct speedtab {
+ int speed;
+ int uxname;
+} speedtab[] = {
+ { 50, B50 },
+ { 75, B75 },
+ { 110, B110 },
+ { 134, B134 },
+ { 150, B150 },
+ { 200, B200 },
+ { 300, B300 },
+ { 600, B600 },
+ { 1200, B1200 },
+ { 1800, B1800 },
+ { 2400, B2400 },
+ { 4800, B4800 },
+ { 9600, B9600 },
+ { 19200, EXTA },
+ { 19, EXTA }, /* for people who say 19.2K */
+ { 38400, EXTB },
+ { 38, EXTB },
+ { 7200, EXTB }, /* alternative */
+ { 57600, B57600 },
+ { 115200, B115200 },
+ { 230400, B230400 },
+ { 0, 0 }
+};
+
+int
+speed(int val)
+{
+ struct speedtab *sp;
+
+ if (val <= B230400)
+ return (val);
+
+ for (sp = speedtab; sp->speed; sp++)
+ if (sp->speed == val)
+ return (sp->uxname);
+
+ return (B300); /* default in impossible cases */
+}
+
+void
+makeenv(char *env[])
+{
+ static char termbuf[128] = "TERM=";
+ char *p, *q;
+ char **ep;
+
+ ep = env;
+ if (TT && *TT) {
+ strlcat(termbuf, TT, sizeof(termbuf));
+ *ep++ = termbuf;
+ }
+ if ((p = EV)) {
+ q = p;
+ while ((q = strchr(q, ','))) {
+ *q++ = '\0';
+ *ep++ = p;
+ p = q;
+ }
+ if (*p)
+ *ep++ = p;
+ }
+ *ep = (char *)0;
+}
+
+/*
+ * This speed select mechanism is written for the Develcon DATASWITCH.
+ * The Develcon sends a string of the form "B{speed}\n" at a predefined
+ * baud rate. This string indicates the user's actual speed.
+ * The routine below returns the terminal type mapped from derived speed.
+ */
+static struct portselect {
+ const char *ps_baud;
+ const char *ps_type;
+} portspeeds[] = {
+ { "B110", "std.110" },
+ { "B134", "std.134" },
+ { "B150", "std.150" },
+ { "B300", "std.300" },
+ { "B600", "std.600" },
+ { "B1200", "std.1200" },
+ { "B2400", "std.2400" },
+ { "B4800", "std.4800" },
+ { "B9600", "std.9600" },
+ { "B19200", "std.19200" },
+ { NULL, NULL }
+};
+
+const char *
+portselector(void)
+{
+ char c, baud[20];
+ const char *type = "default";
+ struct portselect *ps;
+ size_t len;
+
+ alarm(5*60);
+ for (len = 0; len < sizeof (baud) - 1; len++) {
+ if (read(STDIN_FILENO, &c, 1) <= 0)
+ break;
+ c &= 0177;
+ if (c == '\n' || c == '\r')
+ break;
+ if (c == 'B')
+ len = 0; /* in case of leading garbage */
+ baud[len] = c;
+ }
+ baud[len] = '\0';
+ for (ps = portspeeds; ps->ps_baud; ps++)
+ if (strcmp(ps->ps_baud, baud) == 0) {
+ type = ps->ps_type;
+ break;
+ }
+ sleep(2); /* wait for connection to complete */
+ return (type);
+}
+
+/*
+ * This auto-baud speed select mechanism is written for the Micom 600
+ * portselector. Selection is done by looking at how the character '\r'
+ * is garbled at the different speeds.
+ */
+const char *
+autobaud(void)
+{
+ struct pollfd set[1];
+ struct timespec timeout;
+ char c;
+ const char *type = "9600-baud";
+
+ (void)tcflush(0, TCIOFLUSH);
+ set[0].fd = STDIN_FILENO;
+ set[0].events = POLLIN;
+ if (poll(set, 1, 5000) <= 0)
+ return (type);
+ if (read(STDIN_FILENO, &c, sizeof(char)) != sizeof(char))
+ return (type);
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 20000;
+ (void)nanosleep(&timeout, NULL);
+ (void)tcflush(0, TCIOFLUSH);
+ switch (c & 0377) {
+
+ case 0200: /* 300-baud */
+ type = "300-baud";
+ break;
+
+ case 0346: /* 1200-baud */
+ type = "1200-baud";
+ break;
+
+ case 015: /* 2400-baud */
+ case 0215:
+ type = "2400-baud";
+ break;
+
+ default: /* 4800-baud */
+ type = "4800-baud";
+ break;
+
+ case 0377: /* 9600-baud */
+ type = "9600-baud";
+ break;
+ }
+ return (type);
+}
diff --git a/libexec/getty/ttys.5 b/libexec/getty/ttys.5
new file mode 100644
index 000000000000..cd94e6a1c91a
--- /dev/null
+++ b/libexec/getty/ttys.5
@@ -0,0 +1,177 @@
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\" "
+.Dd October 26, 2023
+.Dt TTYS 5
+.Os
+.Sh NAME
+.Nm ttys
+.Nd terminal initialization information
+.Sh DESCRIPTION
+The file
+.Nm
+contains information that is used by various routines to initialize
+and control the use of terminal special files.
+Pseudo-terminals (see
+.Xr pts 4 )
+are not listed.
+This information is read with the
+.Xr getttyent 3
+library routines.
+There is one line in the
+.Nm
+file per special device file.
+Fields are separated by tabs and/or spaces.
+Fields comprised of more than one word should be enclosed in double
+quotes (``"'').
+Blank lines and comments may appear anywhere in the file; comments
+are delimited by hash marks (``#'') and new lines.
+Any unspecified fields will default to null.
+.Pp
+The first field is normally the
+name of the terminal special file as it is found in
+.Pa /dev .
+However, it can be any arbitrary string
+when the associated command is not related to a tty.
+.Pp
+The second field of the file is the command to execute for the line,
+usually
+.Xr getty 8 ,
+which initializes and opens the line, setting the speed, waiting for
+a user name and executing the
+.Xr login 1
+program.
+It can be, however, any desired command, for example
+the start up for a window system terminal emulator or some other
+daemon process, and can contain multiple words if quoted.
+.Pp
+The third field is the type of terminal usually connected to that
+tty line, normally the one found in the
+.Xr termcap 5
+data base file.
+The environment variable
+.Ev TERM
+is initialized with the value by
+either
+.Xr getty 8
+or
+.Xr login 1 .
+.Pp
+The remaining fields set flags in the
+.Fa ty_status
+entry (see
+.Xr getttyent 3 ) ,
+specify a window system process that
+.Xr init 8
+will maintain for the terminal line, optionally determine the
+type of tty (whether dialin, network or otherwise),
+or specify a tty group
+name that allows the login class database (see
+.Xr login.conf 5 )
+to refer to many ttys as a group, to selectively allow or
+deny access or enable or disable accounting facilities for
+ttys as a group.
+.Pp
+As flag values, the strings ``on'' and ``off'' specify that
+.Xr init 8
+should (should not) execute the command given in the second field.
+``onifconsole'' will cause this line to be enabled if and only if it is
+an active kernel console device (it is equivalent to ``on'' in this
+case).
+The flag ``onifexists'' will cause this line to be enabled if and only
+if the name exists.
+If the name starts with a ``/'', it will be considered an absolute
+path.
+Otherwise, it is considered a path relative to
+.Pa /dev .
+The flag ``secure'' (if the console is enabled) allows users with a
+uid of 0 to login on this line.
+The flag ``insecure'' as well as the absence of the ``secure'' flag
+disallows users with uid of 0 to login on this line.
+The flag ``dialup'' indicates that a tty entry describes a dialin
+line, and ``network'' is obsolete and does nothing.
+Either of these strings may also be specified in the terminal type
+field.
+The string ``window='' may be followed by a quoted command
+string which
+.Xr init 8
+will execute
+.Em before
+starting the command specified by the second field.
+.Pp
+The string ``group='' may be followed by a group name comprised of
+alphanumeric characters that can be used by
+.Xr login.conf 5
+to refer to many tty lines as a group to enable or disable access
+and accounting facilities.
+If no group is specified, then the tty becomes a member of the group
+"none".
+For backwards compatibility, the ``group='' should appear last on the
+line, immediately before the optional comment.
+.Pp
+Both the second field and any command specified with ``window=''
+will be split into words and executed using
+.Xr execve 2 .
+Words are separated by any combinations of tabs and spaces.
+Arguments containing whitespace should be enclosed in single quotes
+.Pq Li ' .
+Note that no shell-style globbing or other variable substitution occurs.
+.Sh FILES
+.Bl -tag -width /etc/ttys -compact
+.It Pa /etc/ttys
+.El
+.Sh EXAMPLES
+.Bd -literal
+# root login on console at 1200 baud
+console "/usr/libexec/getty std.1200" vt100 on secure
+# dialup at 1200 baud, no root logins
+ttyd0 "/usr/libexec/getty d1200" dialup on group=dialup # 555-1234
+# Mike's terminal: hp2621
+ttyh0 "/usr/libexec/getty std.9600" hp2621-nl on group=dialup # 457 Evans
+# John's terminal: vt100
+ttyh1 "/usr/libexec/getty std.9600" vt100 on group=dialup # 459 Evans
+# terminal emulate/window system
+ttyv0 "/usr/local/bin/xterm -display :0" xterm on window="/usr/local/bin/X :0"
+.Ed
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr getttyent 3 ,
+.Xr nmdm 4 ,
+.Xr uart 4 ,
+.Xr ucom 4 ,
+.Xr gettytab 5 ,
+.Xr login.conf 5 ,
+.Xr termcap 5 ,
+.Xr getty 8 ,
+.Xr init 8 ,
+.Xr pam_securetty 8 ,
+.Xr pstat 8
+.Sh HISTORY
+A
+.Nm
+file appeared in
+.At v6 .
diff --git a/libexec/hyperv/Makefile b/libexec/hyperv/Makefile
new file mode 100644
index 000000000000..ed0f91c79420
--- /dev/null
+++ b/libexec/hyperv/Makefile
@@ -0,0 +1,9 @@
+.PATH: ${SRCTOP}/contrib/hyperv/tools/scripts
+
+BINDIR= ${LIBEXECDIR}/hyperv
+
+PACKAGE= hyperv-tools
+SCRIPTS= hv_set_ifconfig hv_get_dns_info hv_get_dhcp_info
+SCRIPTS+= hyperv_vfattach hyperv_vfup
+
+.include <bsd.prog.mk>
diff --git a/libexec/hyperv/Makefile.depend b/libexec/hyperv/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/hyperv/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/mail.local/Makefile b/libexec/mail.local/Makefile
new file mode 100644
index 000000000000..d41469068c3b
--- /dev/null
+++ b/libexec/mail.local/Makefile
@@ -0,0 +1,29 @@
+PACKAGE=sendmail
+SENDMAIL_DIR=${SRCTOP}/contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/mail.local
+
+PROG= mail.local
+SRCS= mail.local.c
+MAN= mail.local.8
+CFLAGS+=-I${SENDMAIL_DIR}/include -I.
+
+WARNS?= 2
+WFORMAT=0
+
+LIBADD= sm
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h: ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h .NOMETA
+ ln -sf ${.ALLSRC} ${.TARGET}
+
+.include <bsd.prog.mk>
+
+CWARNFLAGS+= ${NO_WDEPRECATED_NON_PROTOTYPE}
diff --git a/libexec/mail.local/Makefile.depend b/libexec/mail.local/Makefile.depend
new file mode 100644
index 000000000000..681ab960e95c
--- /dev/null
+++ b/libexec/mail.local/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libsm \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/makewhatis.local/Makefile b/libexec/makewhatis.local/Makefile
new file mode 100644
index 000000000000..b541dc8e4de1
--- /dev/null
+++ b/libexec/makewhatis.local/Makefile
@@ -0,0 +1,6 @@
+PACKAGE= mandoc
+SCRIPTS= makewhatis.local.sh
+MAN= makewhatis.local.8
+SCRIPTSDIR= ${LIBEXECDIR}
+
+.include <bsd.prog.mk>
diff --git a/libexec/makewhatis.local/Makefile.depend b/libexec/makewhatis.local/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/makewhatis.local/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/makewhatis.local/makewhatis.local.8 b/libexec/makewhatis.local/makewhatis.local.8
new file mode 100644
index 000000000000..7f50d05c2103
--- /dev/null
+++ b/libexec/makewhatis.local/makewhatis.local.8
@@ -0,0 +1,67 @@
+.\" Copyright (c) April 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.Dd April 26, 1996
+.Dt MAKEWHATIS.LOCAL 8
+.Os
+.Sh NAME
+.Nm makewhatis.local
+.Nd start makewhatis for local file systems
+.Sh SYNOPSIS
+.Nm /usr/libexec/makewhatis.local
+.Op options
+.Ar directories ...
+.Sh DESCRIPTION
+The
+.Nm
+utility starts
+.Xr makewhatis 1
+only for file systems physically mounted on the system
+where
+.Nm
+is being executed.
+Running makewhatis
+by
+.Pa periodic weekly
+for rw nfs-mounted /usr may kill
+your NFS server -- all NFS clients start makewhatis at the same time!
+So use this wrapper for
+.Xr cron 8
+instead of calling makewhatis directly.
+.Sh FILES
+.Bl -tag -width /etc/periodic/weekly/320.whatis.XXX -compact
+.It Pa /etc/periodic/weekly/320.whatis
+run
+.Nm
+every week
+.El
+.Sh SEE ALSO
+.Xr find 1 ,
+.Xr makewhatis 1 ,
+.Xr cron 8 ,
+.Xr periodic 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 2.2 .
diff --git a/libexec/makewhatis.local/makewhatis.local.sh b/libexec/makewhatis.local/makewhatis.local.sh
new file mode 100644
index 000000000000..ace6653852a1
--- /dev/null
+++ b/libexec/makewhatis.local/makewhatis.local.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Copyright (c) April 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# makewhatis.local - start makewhatis(1) only for file systems
+# physically mounted on the system
+#
+# Running makewhatis from /etc/periodic/weekly/320.whatis for rw nfs-mounted
+# /usr may kill your NFS server -- all clients start makewhatis at the same
+# time! So use this wrapper instead calling makewhatis directly.
+#
+
+PATH=/bin:/usr/bin:$PATH; export PATH
+opt= dirs= localdirs=
+
+for arg
+do
+ case "$arg" in
+ -*) opt="$opt $arg";;
+ *) dirs="$dirs $arg";;
+ esac
+done
+
+dirs=`echo $dirs | sed 's/:/ /g'`
+case X"$dirs" in X) echo "usage: $0 [options] directories ..."; exit 1;; esac
+
+localdirs=`find -H $dirs -fstype local \! -fstype rdonly -type d -prune -print`
+
+case X"$localdirs" in
+ X) echo "$0: no local rw-mounted manual directories found: $dirs"
+ exit 1;;
+ *) exec `basename $0 .local` $opt $localdirs;;
+esac
diff --git a/libexec/mknetid/Makefile b/libexec/mknetid/Makefile
new file mode 100644
index 000000000000..c872abd74b9c
--- /dev/null
+++ b/libexec/mknetid/Makefile
@@ -0,0 +1,8 @@
+PROG= mknetid
+SRCS= mknetid.c hash.c parse_group.c
+
+MAN= netid.5 mknetid.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/libexec/mknetid/Makefile.depend b/libexec/mknetid/Makefile.depend
new file mode 100644
index 000000000000..a2d89550fa2b
--- /dev/null
+++ b/libexec/mknetid/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/rpc \
+ include/rpcsvc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/mknetid/hash.c b/libexec/mknetid/hash.c
new file mode 100644
index 000000000000..5375b80fbe3b
--- /dev/null
+++ b/libexec/mknetid/hash.c
@@ -0,0 +1,165 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "hash.h"
+
+/*
+ * This hash function is stolen directly from the
+ * Berkeley DB package. It already exists inside libc, but
+ * it's declared static which prevents us from calling it
+ * from here.
+ */
+/*
+ * OZ's original sdbm hash
+ */
+u_int32_t
+hash(const void *keyarg, size_t len)
+{
+ const u_char *key;
+ size_t loop;
+ u_int32_t h;
+
+#define HASHC h = *key++ + 65599 * h
+
+ h = 0;
+ key = keyarg;
+ if (len > 0) {
+ loop = (len + 8 - 1) >> 3;
+
+ switch (len & (8 - 1)) {
+ case 0:
+ do {
+ HASHC;
+ /* FALLTHROUGH */
+ case 7:
+ HASHC;
+ /* FALLTHROUGH */
+ case 6:
+ HASHC;
+ /* FALLTHROUGH */
+ case 5:
+ HASHC;
+ /* FALLTHROUGH */
+ case 4:
+ HASHC;
+ /* FALLTHROUGH */
+ case 3:
+ HASHC;
+ /* FALLTHROUGH */
+ case 2:
+ HASHC;
+ /* FALLTHROUGH */
+ case 1:
+ HASHC;
+ } while (--loop);
+ }
+ }
+ return (h);
+}
+
+/*
+ * Generate a hash value for a given key (character string).
+ * We mask off all but the lower 8 bits since our table array
+ * can only hole 256 elements.
+ */
+u_int32_t hashkey(char *key)
+{
+
+ if (key == NULL)
+ return (-1);
+ return(hash((void *)key, strlen(key)) & HASH_MASK);
+}
+
+/* Find an entry in the hash table (may be hanging off a linked list). */
+struct grouplist *lookup(struct member_entry *table[], char *key)
+{
+ struct member_entry *cur;
+
+ cur = table[hashkey(key)];
+
+ while (cur) {
+ if (!strcmp(cur->key, key))
+ return(cur->groups);
+ cur = cur->next;
+ }
+
+ return(NULL);
+}
+
+struct grouplist dummy = { 99999, NULL };
+
+/*
+ * Store a group member entry and/or update its grouplist.
+ */
+void mstore (struct member_entry *table[], char *key, int gid, int dup)
+{
+ struct member_entry *cur, *new;
+ struct grouplist *tmp;
+ u_int32_t i;
+
+ i = hashkey(key);
+ cur = table[i];
+
+ if (!dup) {
+ tmp = (struct grouplist *)malloc(sizeof(struct grouplist));
+ tmp->groupid = gid;
+ tmp->next = NULL;
+ }
+
+ /* Check if all we have to do is insert a new groupname. */
+ while (cur) {
+ if (!dup && !strcmp(cur->key, key)) {
+ tmp->next = cur->groups;
+ cur->groups = tmp;
+ return;
+ }
+ cur = cur->next;
+ }
+
+ /* Didn't find a match -- add the whole mess to the table. */
+ new = (struct member_entry *)malloc(sizeof(struct member_entry));
+ new->key = strdup(key);
+ if (!dup)
+ new->groups = tmp;
+ else
+ new->groups = (struct grouplist *)&dummy;
+ new->next = table[i];
+ table[i] = new;
+
+ return;
+}
diff --git a/libexec/mknetid/hash.h b/libexec/mknetid/hash.h
new file mode 100644
index 000000000000..24b9ecb483df
--- /dev/null
+++ b/libexec/mknetid/hash.h
@@ -0,0 +1,54 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Groupid entry hung off a member_entry node. */
+struct grouplist {
+ gid_t groupid;
+ struct grouplist *next;
+};
+
+/* Entry in the cooked member list hash table. */
+struct member_entry {
+ char *key; /* username */
+ struct grouplist *groups;
+ struct member_entry *next;
+};
+
+/* Table size (chosen arbitrarily). Not too big, not too small. */
+#define TABLESIZE 1024
+#define HASH_MASK 0x000003FF
+
+extern void mstore(struct member_entry ** , char *, int, int);
+extern struct grouplist *lookup(struct member_entry **, char *);
+
diff --git a/libexec/mknetid/mknetid.8 b/libexec/mknetid/mknetid.8
new file mode 100644
index 000000000000..5533519cec8d
--- /dev/null
+++ b/libexec/mknetid/mknetid.8
@@ -0,0 +1,150 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd June 23, 1996
+.Dt MKNETID 8
+.Os
+.Sh NAME
+.Nm mknetid
+.Nd "generate netid map data"
+.Sh SYNOPSIS
+.Nm
+.Op Fl q
+.Op Fl g Ar group_file
+.Op Fl p Ar passwd_file
+.Op Fl h Ar hosts_file
+.Op Fl n Ar netid_file
+.Op Fl d Ar domain
+.Sh DESCRIPTION
+The
+.Nm
+utility processes the contents of the
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr hosts 5
+and
+.Xr netid 5
+files into the format used to generate the
+.Pa netid.byname
+.Tn NIS
+map.
+This map is used to hold credential information for both users
+and hosts in an operating system independent format.
+.Pp
+The
+.Nm
+utility checks for duplicate occurrences of netids and filters
+them out.
+.Pp
+The
+.Nm
+utility prints its results on the standard output.
+It is usually called
+only by
+.Pa /var/yp/Makefile
+when rebuilding the
+.Tn NIS
+maps.
+.Sh OPTIONS
+The
+.Nm
+utility supports the following options:
+.Bl -tag -width indent
+.It Fl q
+Normally,
+.Nm
+prints a warning message when it encounters a duplicate netid.
+This flag turns on 'quiet' mode, allowing the warnings to be
+suppressed.
+Other error messages may still be generated.
+.It Fl g Ar group_file
+Specify the location of the group information
+file.
+The compiled-in default is
+.Pa /etc/group .
+.It Fl p Ar passwd_file
+Specify the location of the passwd information
+file.
+The compiled-in default is
+.Pa /etc/passwd .
+.It Fl h Ar hosts_file
+Specify the location of the hosts database
+file.
+The compiled-in default is
+.Pa /etc/hosts .
+.It Fl n Ar netid_file
+Specify the location of the netid information
+file.
+The compiled-in default is
+.Pa /etc/netid .
+Note that no error is generated if the netid database cannot be
+found.
+The netid database is not likely to be present on most systems
+until
+.Tn Secure RPC
+support is added to
+.Fx .
+.It Fl d Ar domain
+By default, the
+.Nm
+utility uses the system domainname when generating netid records.
+If
+the system domainname is not set, the domain must be specified on the
+command line with the
+.Fl d
+flag.
+If the domainname is set, the
+.Fl d
+flag may be used to override it.
+.El
+.Sh FILES
+.Bl -tag -width /var/yp/Makefile -compact
+.It Pa /var/yp/Makefile
+the Makefile that calls
+.Nm yp_mkdb
+and
+.Nm
+to build the
+.Tn NIS
+databases
+.It Pa /etc/group
+the default group database file
+.It Pa /etc/passwd
+the default passwd database file
+.It Pa /etc/hosts
+the default hosts database file
+.It Pa /etc/netid
+the default netid database file
+.El
+.Sh SEE ALSO
+.Xr yp 8 ,
+.Xr yp_mkdb 8
+.Sh AUTHORS
+.An Bill Paul Aq Mt wpaul@ctr.columbia.edu
diff --git a/libexec/mknetid/mknetid.c b/libexec/mknetid/mknetid.c
new file mode 100644
index 000000000000..a5c8281ef34d
--- /dev/null
+++ b/libexec/mknetid/mknetid.c
@@ -0,0 +1,303 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * netid map generator program
+ *
+ * Written by Bill Paul <wpaul@ctr.columbia.edu>
+ * Center for Telecommunications Research
+ * Columbia University, New York City
+ */
+
+#include <sys/types.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+#include <err.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hash.h"
+
+#define LINSIZ 1024
+#define OPSYS "unix"
+
+/* Default location of group file. */
+char *groupfile = _PATH_GROUP;
+/* Default location of master.passwd file. */
+char *passfile = _PATH_PASSWD;
+/* Default location of hosts file. */
+char *hostsfile = _PATH_HOSTS;
+/* Default location of netid file */
+char *netidfile = "/etc/netid";
+
+/*
+ * Stored hash table of 'reverse' group member database
+ * which we will construct.
+ */
+struct member_entry *mtable[TABLESIZE];
+
+/*
+ * Dupe table: used to keep track of entries so we don't
+ * print the same thing twice.
+ */
+struct member_entry *dtable[TABLESIZE];
+
+extern struct group *_getgrent(void);
+extern int _setgrent(void);
+extern void _endgrent(void);
+
+static void
+usage(void)
+{
+ fprintf (stderr, "%s\n%s\n",
+ "usage: mknetid [-q] [-g group_file] [-p passwd_file] [-h hosts_file]",
+ " [-n netid_file] [-d domain]");
+ exit(1);
+}
+
+extern FILE *_gr_fp;
+
+int
+main(int argc, char *argv[])
+{
+ FILE *gfp, *pfp, *hfp, *nfp;
+ char readbuf[LINSIZ];
+ char writebuf[LINSIZ];
+ struct group *gr;
+ struct grouplist *glist;
+ char *domain;
+ int ch;
+ gid_t i;
+ char *ptr, *pidptr, *gidptr, *hptr;
+ int quiet = 0;
+
+ domain = NULL;
+ while ((ch = getopt(argc, argv, "g:p:h:n:d:q")) != -1) {
+ switch(ch) {
+ case 'g':
+ groupfile = optarg;
+ break;
+ case 'p':
+ passfile = optarg;
+ break;
+ case 'h':
+ hostsfile = optarg;
+ break;
+ case 'n':
+ netidfile = optarg;
+ break;
+ case 'd':
+ domain = optarg;
+ break;
+ case 'q':
+ quiet++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (domain == NULL) {
+ if (yp_get_default_domain(&domain))
+ errx(1, "no domain name specified and default \
+domain not set");
+ }
+
+ if ((gfp = fopen(groupfile, "r")) == NULL) {
+ err(1, "%s", groupfile);
+ }
+
+ if ((pfp = fopen(passfile, "r")) == NULL) {
+ err(1, "%s", passfile);
+ }
+
+ if ((hfp = fopen(hostsfile, "r")) == NULL) {
+ err(1, "%s", hostsfile);
+ }
+
+ if ((nfp = fopen(netidfile, "r")) == NULL) {
+ /* netid is optional -- just continue */
+ nfp = NULL;
+ }
+
+ _gr_fp = gfp;
+
+ /* Load all the group membership info into a hash table. */
+
+ _setgrent();
+ while((gr = _getgrent()) != NULL) {
+ while(*gr->gr_mem) {
+ mstore(mtable, *gr->gr_mem, gr->gr_gid, 0);
+ gr->gr_mem++;
+ }
+ }
+
+ fclose(gfp);
+ _endgrent();
+
+ /*
+ * Now parse the passwd database, spewing out the extra
+ * group information we just stored if necessary.
+ */
+ while(fgets(readbuf, LINSIZ, pfp)) {
+ /* Ignore comments: ^[ \t]*# */
+ for (ptr = readbuf; *ptr != '\0'; ptr++)
+ if (*ptr != ' ' && *ptr != '\t')
+ break;
+ if (*ptr == '#' || *ptr == '\0')
+ continue;
+ if ((ptr = strchr(readbuf, ':')) == NULL) {
+ warnx("bad passwd file entry: %s", readbuf);
+ continue;
+ }
+ *ptr = '\0';
+ ptr++;
+ if ((ptr = strchr(ptr, ':')) == NULL) {
+ warnx("bad passwd file entry: %s", readbuf);
+ continue;
+ }
+ *ptr = '\0';
+ ptr++;
+ pidptr = ptr;
+ if ((ptr = strchr(ptr, ':')) == NULL) {
+ warnx("bad passwd file entry: %s", readbuf);
+ continue;
+ }
+ *ptr = '\0';
+ ptr++;
+ gidptr = ptr;
+ if ((ptr = strchr(ptr, ':')) == NULL) {
+ warnx("bad passwd file entry: %s", readbuf);
+ continue;
+ }
+ *ptr = '\0';
+ i = atol(gidptr);
+
+ snprintf(writebuf, sizeof(writebuf), "%s.%s@%s", OPSYS,
+ pidptr, domain);
+
+ if (lookup(dtable, writebuf)) {
+ if (!quiet)
+ warnx("duplicate netid '%s.%s@%s' -- skipping",
+ OPSYS, pidptr, domain);
+ continue;
+ } else {
+ mstore(dtable, writebuf, 0, 1);
+ }
+ printf("%s.%s@%s %s:%s", OPSYS, pidptr, domain, pidptr, gidptr);
+ if ((glist = lookup(mtable, (char *)&readbuf)) != NULL) {
+ while(glist) {
+ if (glist->groupid != i)
+ printf(",%lu", (u_long)glist->groupid);
+ glist = glist->next;
+ }
+ }
+ printf ("\n");
+ }
+
+ fclose(pfp);
+
+ /*
+ * Now parse the hosts database (this part sucks).
+ */
+
+ while ((ptr = fgets(readbuf, LINSIZ, hfp))) {
+ if (*ptr == '#')
+ continue;
+ if (!(hptr = strpbrk(ptr, "#\n")))
+ continue;
+ *hptr = '\0';
+ if (!(hptr = strpbrk(ptr, " \t")))
+ continue;
+ *hptr++ = '\0';
+ ptr = hptr;
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+ if (!(hptr = strpbrk(ptr, " \t")))
+ continue;
+ *hptr++ = '\0';
+ snprintf(writebuf, sizeof(writebuf), "%s.%s@%s", OPSYS,
+ ptr, domain);
+ if (lookup(dtable, (char *)&writebuf)) {
+ if (!quiet)
+ warnx("duplicate netid '%s' -- skipping",
+ writebuf);
+ continue;
+ } else {
+ mstore(dtable, (char *)&writebuf, 0, 1);
+ }
+ printf ("%s.%s@%s 0:%s\n", OPSYS, ptr, domain, ptr);
+ }
+
+ fclose(hfp);
+
+ /*
+ * Lastly, copy out any extra information in the netid
+ * file. If it's not open, just ignore it: it's optional anyway.
+ */
+
+ if (nfp != NULL) {
+ while(fgets(readbuf, LINSIZ, nfp)) {
+ if (readbuf[0] == '#')
+ continue;
+ if ((ptr = strpbrk((char*)&readbuf, " \t")) == NULL) {
+ warnx("bad netid entry: '%s'", readbuf);
+ continue;
+ }
+
+ writebuf[0] = *ptr;
+ *ptr = '\0';
+ if (lookup(dtable, (char *)&readbuf)) {
+ if (!quiet)
+ warnx("duplicate netid '%s' -- skipping",
+ readbuf);
+ continue;
+ } else {
+ mstore(dtable, (char *)&readbuf, 0, 1);
+ }
+ *ptr = writebuf[0];
+ printf("%s",readbuf);
+ }
+ fclose(nfp);
+ }
+
+ exit(0);
+}
diff --git a/libexec/mknetid/netid.5 b/libexec/mknetid/netid.5
new file mode 100644
index 000000000000..a5635201dcc7
--- /dev/null
+++ b/libexec/mknetid/netid.5
@@ -0,0 +1,89 @@
+.\" Copyright (c) 1996 Mats O Jansson <moj@stacken.kth.se>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Mats O Jansson
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 13, 1996
+.Dt NETID 5
+.Os
+.Sh NAME
+.Nm netid
+.Nd
+.Tn YP
+network credential file
+.Sh DESCRIPTION
+Files in
+.Nm
+format are rare.
+One lives in the
+.Tn YP
+map
+.Pa netid.byname .
+The format is rather simple.
+Each row consists of two items: a key and a value.
+When created by
+.Xr mknetid 8
+there are three types of records.
+.Pp
+The first type is information about which GIDs a UID has:
+.Pp
+.Sm off
+.Li unix . Ao Ar uid Ac @ Aq Ar yp-domain
+.Sm on
+.Sm off
+.Ao Ar uid Ac : Ao Ar gid Ac , Aq Ar gid
+.Sm on
+.Pp
+The second type contains information about hosts:
+.Pp
+.Sm off
+.Li unix . Ao Ar hostname Ac @ Aq Ar yp-domain
+.Sm on
+.Sm off
+.Li 0 : Aq Ar hostname
+.Sm on
+.Pp
+The third type refers to records from a
+.Nm
+file other than the two types above.
+.Sh FILES
+.Bl -tag -width ".Pa /etc/netid" -compact
+.It Pa /etc/netid
+for lines not generated automatically by
+.Xr mknetid 8
+.El
+.Sh EXAMPLES
+A configuration file might look like the following:
+.Bd -literal
+unix.10714@kaka 10714:400,10
+unix.jodie@kaka 0:jodie
+.Ed
+.Sh SEE ALSO
+.Xr mknetid 8 ,
+.Xr yp 8
+.Sh AUTHORS
+.An Mats O Jansson Aq Mt moj@stacken.kth.se
diff --git a/libexec/mknetid/parse_group.c b/libexec/mknetid/parse_group.c
new file mode 100644
index 000000000000..c385dfad5190
--- /dev/null
+++ b/libexec/mknetid/parse_group.c
@@ -0,0 +1,151 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This is a slightly modified chunk of getgrent(3). All the YP support
+ * and unneeded functions have been stripped out.
+ */
+
+#include <sys/types.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+FILE *_gr_fp;
+static struct group _gr_group;
+static int _gr_stayopen;
+static int grscan(int, int);
+static int start_gr(void);
+
+#define MAXGRP 200
+static char *members[MAXGRP];
+#define MAXLINELENGTH 1024
+static char line[MAXLINELENGTH];
+
+struct group *
+_getgrent(void)
+{
+ if (!_gr_fp && !start_gr()) {
+ return NULL;
+ }
+
+
+ if (!grscan(0, 0))
+ return(NULL);
+ return(&_gr_group);
+}
+
+static int
+start_gr(void)
+{
+ return 1;
+}
+
+int
+_setgroupent(int stayopen)
+{
+ if (!start_gr())
+ return(0);
+ _gr_stayopen = stayopen;
+ return(1);
+}
+
+int
+_setgrent(void)
+{
+ return(_setgroupent(0));
+}
+
+void
+_endgrent(void)
+{
+ if (_gr_fp) {
+ (void)fclose(_gr_fp);
+ _gr_fp = NULL;
+ }
+}
+
+static int
+grscan(int search, int gid)
+{
+ char *cp, **m;
+ char *bp;
+ for (;;) {
+ if (!fgets(line, sizeof(line), _gr_fp))
+ return(0);
+ bp = line;
+ /* skip lines that are too big */
+ if (!strchr(line, '\n')) {
+ int ch;
+
+ while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
+ ;
+ continue;
+ }
+ if ((_gr_group.gr_name = strsep(&bp, ":\n")) == NULL)
+ break;
+ if (_gr_group.gr_name[0] == '+')
+ continue;
+ if ((_gr_group.gr_passwd = strsep(&bp, ":\n")) == NULL)
+ break;
+ if (!(cp = strsep(&bp, ":\n")))
+ continue;
+ _gr_group.gr_gid = atoi(cp);
+ if (search && _gr_group.gr_gid != gid)
+ continue;
+ cp = NULL;
+ if (bp == NULL) /* !! Must check for this! */
+ break;
+ for (m = _gr_group.gr_mem = members;; bp++) {
+ if (m == &members[MAXGRP - 1])
+ break;
+ if (*bp == ',') {
+ if (cp) {
+ *bp = '\0';
+ *m++ = cp;
+ cp = NULL;
+ }
+ } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
+ if (cp) {
+ *bp = '\0';
+ *m++ = cp;
+ }
+ break;
+ } else if (cp == NULL)
+ cp = bp;
+ }
+ *m = NULL;
+ return(1);
+ }
+ /* NOTREACHED */
+ return (0);
+}
diff --git a/libexec/nuageinit/Makefile b/libexec/nuageinit/Makefile
new file mode 100644
index 000000000000..755ecb7ff418
--- /dev/null
+++ b/libexec/nuageinit/Makefile
@@ -0,0 +1,12 @@
+PACKAGE= nuageinit
+SCRIPTS= nuageinit
+FILES= nuage.lua
+FILESDIR= ${SHAREDIR}/flua
+MAN= nuageinit.7
+
+.include <src.opts.mk>
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
+.include <bsd.prog.mk>
diff --git a/libexec/nuageinit/Makefile.depend b/libexec/nuageinit/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/nuageinit/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/nuageinit/nuage.lua b/libexec/nuageinit/nuage.lua
new file mode 100644
index 000000000000..3eeb2ea0b44c
--- /dev/null
+++ b/libexec/nuageinit/nuage.lua
@@ -0,0 +1,710 @@
+---
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright(c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org>
+-- Copyright(c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org>
+
+local unistd = require("posix.unistd")
+local sys_stat = require("posix.sys.stat")
+local lfs = require("lfs")
+
+local function getlocalbase()
+ local f = io.popen("sysctl -in user.localbase 2> /dev/null")
+ local localbase = f:read("*l")
+ f:close()
+ if localbase == nil or localbase:len() == 0 then
+ -- fallback
+ localbase = "/usr/local"
+ end
+ return localbase
+end
+
+local function decode_base64(input)
+ local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ input = string.gsub(input, '[^'..b..'=]', '')
+
+ local result = {}
+ local bits = ''
+
+ -- convert all characters in bits
+ for i = 1, #input do
+ local x = input:sub(i, i)
+ if x == '=' then
+ break
+ end
+ local f = b:find(x) - 1
+ for j = 6, 1, -1 do
+ bits = bits .. (f % 2^j - f % 2^(j-1) > 0 and '1' or '0')
+ end
+ end
+
+ for i = 1, #bits, 8 do
+ local byte = bits:sub(i, i + 7)
+ if #byte == 8 then
+ local c = 0
+ for j = 1, 8 do
+ c = c + (byte:sub(j, j) == '1' and 2^(8 - j) or 0)
+ end
+ table.insert(result, string.char(c))
+ end
+ end
+
+ return table.concat(result)
+end
+
+local function warnmsg(str, prepend)
+ if not str then
+ return
+ end
+ local tag = ""
+ if prepend ~= false then
+ tag = "nuageinit: "
+ end
+ io.stderr:write(tag .. str .. "\n")
+end
+
+local function errmsg(str, prepend)
+ warnmsg(str, prepend)
+ os.exit(1)
+end
+
+local function chmod(path, mode)
+ local mode = tonumber(mode, 8)
+ local _, err, msg = sys_stat.chmod(path, mode)
+ if err then
+ errmsg("chmod(" .. path .. ", " .. mode .. ") failed: " .. msg)
+ end
+end
+
+local function chown(path, owner, group)
+ local _, err, msg = unistd.chown(path, owner, group)
+ if err then
+ errmsg("chown(" .. path .. ", " .. owner .. ", " .. group .. ") failed: " .. msg)
+ end
+end
+
+local function dirname(oldpath)
+ if not oldpath then
+ return nil
+ end
+ local path = oldpath:gsub("[^/]+/*$", "")
+ if path == "" then
+ return nil
+ end
+ return path
+end
+
+local function mkdir_p(path)
+ if lfs.attributes(path, "mode") ~= nil then
+ return true
+ end
+ local r, err = mkdir_p(dirname(path))
+ if not r then
+ return nil, err .. " (creating " .. path .. ")"
+ end
+ return lfs.mkdir(path)
+end
+
+local function sethostname(hostname)
+ if hostname == nil then
+ return
+ end
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ if not root then
+ root = ""
+ end
+ local hostnamepath = root .. "/etc/rc.conf.d/hostname"
+
+ mkdir_p(dirname(hostnamepath))
+ local f, err = io.open(hostnamepath, "w")
+ if not f then
+ warnmsg("Impossible to open " .. hostnamepath .. ":" .. err)
+ return
+ end
+ f:write('hostname="' .. hostname .. '"\n')
+ f:close()
+end
+
+local function splitlist(list)
+ local ret = {}
+ if type(list) == "string" then
+ for str in list:gmatch("([^, ]+)") do
+ ret[#ret + 1] = str
+ end
+ elseif type(list) == "table" then
+ ret = list
+ else
+ warnmsg("Invalid type " .. type(list) .. ", expecting table or string")
+ end
+ return ret
+end
+
+local function splitlines(s)
+ local ret = {}
+
+ for line in string.gmatch(s, "[^\n]+") do
+ ret[#ret + 1] = line
+ end
+
+ return ret
+end
+
+local function getgroups()
+ local ret = {}
+
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ local cmd = "pw "
+ if root then
+ cmd = cmd .. "-R " .. root .. " "
+ end
+
+ local f = io.popen(cmd .. "groupshow -a 2> /dev/null | cut -d: -f1")
+ local groups = f:read("*a")
+ f:close()
+
+ return splitlines(groups)
+end
+
+local function checkgroup(group)
+ local groups = getgroups()
+
+ for _, group2chk in ipairs(groups) do
+ if group == group2chk then
+ return true
+ end
+ end
+
+ return false
+end
+
+local function purge_group(groups)
+ local ret = {}
+
+ for _, group in ipairs(groups) do
+ if checkgroup(group) then
+ ret[#ret + 1] = group
+ else
+ warnmsg("ignoring non-existent group '" .. group .. "'")
+ end
+ end
+
+ return ret
+end
+
+local function adduser(pwd)
+ if (type(pwd) ~= "table") then
+ warnmsg("Argument should be a table")
+ return nil
+ end
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ local cmd = "pw "
+ if root then
+ cmd = cmd .. "-R " .. root .. " "
+ end
+ local f = io.popen(cmd .. " usershow " .. pwd.name .. " -7 2> /dev/null")
+ local pwdstr = f:read("*a")
+ f:close()
+ if pwdstr:len() ~= 0 then
+ return pwdstr:match("%a+:.+:%d+:%d+:.*:(.*):.*")
+ end
+ if not pwd.gecos then
+ pwd.gecos = pwd.name .. " User"
+ end
+ if not pwd.homedir then
+ pwd.homedir = "/home/" .. pwd.name
+ end
+ local extraargs = ""
+ if pwd.groups then
+ local list = splitlist(pwd.groups)
+ -- pw complains if the group does not exist, so if the user
+ -- specifies one that cannot be found, nuageinit will generate
+ -- an exception and exit, unlike cloud-init, which only issues
+ -- a warning but creates the user anyway.
+ list = purge_group(list)
+ if #list > 0 then
+ extraargs = " -G " .. table.concat(list, ",")
+ end
+ end
+ -- pw will automatically create a group named after the username
+ -- do not add a -g option in this case
+ if pwd.primary_group and pwd.primary_group ~= pwd.name then
+ extraargs = extraargs .. " -g " .. pwd.primary_group
+ end
+ if not pwd.no_create_home then
+ extraargs = extraargs .. " -m "
+ end
+ if not pwd.shell then
+ pwd.shell = "/bin/sh"
+ end
+ local precmd = ""
+ local postcmd = ""
+ local input = nil
+ if pwd.passwd then
+ input = pwd.passwd
+ postcmd = " -H 0"
+ elseif pwd.plain_text_passwd then
+ input = pwd.plain_text_passwd
+ postcmd = " -h 0"
+ end
+ cmd = precmd .. "pw "
+ if root then
+ cmd = cmd .. "-R " .. root .. " "
+ end
+ cmd = cmd .. "useradd -n " .. pwd.name .. " -M 0755 -w none "
+ cmd = cmd .. extraargs .. " -c '" .. pwd.gecos
+ cmd = cmd .. "' -d '" .. pwd.homedir .. "' -s " .. pwd.shell .. postcmd
+
+ f = io.popen(cmd, "w")
+ if input then
+ f:write(input)
+ end
+ local r = f:close(cmd)
+ if not r then
+ warnmsg("fail to add user " .. pwd.name)
+ warnmsg(cmd)
+ return nil
+ end
+ if pwd.locked then
+ cmd = "pw "
+ if root then
+ cmd = cmd .. "-R " .. root .. " "
+ end
+ cmd = cmd .. "lock " .. pwd.name
+ os.execute(cmd)
+ end
+ return pwd.homedir
+end
+
+local function addgroup(grp)
+ if (type(grp) ~= "table") then
+ warnmsg("Argument should be a table")
+ return false
+ end
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ local cmd = "pw "
+ if root then
+ cmd = cmd .. "-R " .. root .. " "
+ end
+ local f = io.popen(cmd .. " groupshow " .. grp.name .. " 2> /dev/null")
+ local grpstr = f:read("*a")
+ f:close()
+ if grpstr:len() ~= 0 then
+ return true
+ end
+ local extraargs = ""
+ if grp.members then
+ local list = splitlist(grp.members)
+ extraargs = " -M " .. table.concat(list, ",")
+ end
+ cmd = "pw "
+ if root then
+ cmd = cmd .. "-R " .. root .. " "
+ end
+ cmd = cmd .. "groupadd -n " .. grp.name .. extraargs
+ local r = os.execute(cmd)
+ if not r then
+ warnmsg("fail to add group " .. grp.name)
+ warnmsg(cmd)
+ return false
+ end
+ return true
+end
+
+local function addsshkey(homedir, key)
+ local chownak = false
+ local chowndotssh = false
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ if root then
+ homedir = root .. "/" .. homedir
+ end
+ local ak_path = homedir .. "/.ssh/authorized_keys"
+ local dotssh_path = homedir .. "/.ssh"
+ local dirattrs = lfs.attributes(ak_path)
+ if dirattrs == nil then
+ chownak = true
+ dirattrs = lfs.attributes(dotssh_path)
+ if dirattrs == nil then
+ assert(lfs.mkdir(dotssh_path))
+ chowndotssh = true
+ dirattrs = lfs.attributes(homedir)
+ end
+ end
+
+ local f = io.open(ak_path, "a")
+ if not f then
+ warnmsg("impossible to open " .. ak_path)
+ return
+ end
+ f:write(key .. "\n")
+ f:close()
+ if chownak then
+ chmod(ak_path, "0600")
+ chown(ak_path, dirattrs.uid, dirattrs.gid)
+ end
+ if chowndotssh then
+ chmod(dotssh_path, "0700")
+ chown(dotssh_path, dirattrs.uid, dirattrs.gid)
+ end
+end
+
+local function adddoas(pwd)
+ local chmodetcdir = false
+ local chmoddoasconf = false
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ local localbase = getlocalbase()
+ local etcdir = localbase .. "/etc"
+ if root then
+ etcdir= root .. etcdir
+ end
+ local doasconf = etcdir .. "/doas.conf"
+ local doasconf_attr = lfs.attributes(doasconf)
+ if doasconf_attr == nil then
+ chmoddoasconf = true
+ local dirattrs = lfs.attributes(etcdir)
+ if dirattrs == nil then
+ local r, err = mkdir_p(etcdir)
+ if not r then
+ return nil, err .. " (creating " .. etcdir .. ")"
+ end
+ chmodetcdir = true
+ end
+ end
+ local f = io.open(doasconf, "a")
+ if not f then
+ warnmsg("impossible to open " .. doasconf)
+ return
+ end
+ if type(pwd.doas) == "string" then
+ local rule = pwd.doas
+ rule = rule:gsub("%%u", pwd.name)
+ f:write(rule .. "\n")
+ elseif type(pwd.doas) == "table" then
+ for _, str in ipairs(pwd.doas) do
+ local rule = str
+ rule = rule:gsub("%%u", pwd.name)
+ f:write(rule .. "\n")
+ end
+ end
+ f:close()
+ if chmoddoasconf then
+ chmod(doasconf, "0640")
+ end
+ if chmodetcdir then
+ chmod(etcdir, "0755")
+ end
+end
+
+local function addsudo(pwd)
+ local chmodsudoersd = false
+ local chmodsudoers = false
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ local localbase = getlocalbase()
+ local sudoers_dir = localbase .. "/etc/sudoers.d"
+ if root then
+ sudoers_dir= root .. sudoers_dir
+ end
+ local sudoers = sudoers_dir .. "/90-nuageinit-users"
+ local sudoers_attr = lfs.attributes(sudoers)
+ if sudoers_attr == nil then
+ chmodsudoers = true
+ local dirattrs = lfs.attributes(sudoers_dir)
+ if dirattrs == nil then
+ local r, err = mkdir_p(sudoers_dir)
+ if not r then
+ return nil, err .. " (creating " .. sudoers_dir .. ")"
+ end
+ chmodsudoersd = true
+ end
+ end
+ local f = io.open(sudoers, "a")
+ if not f then
+ warnmsg("impossible to open " .. sudoers)
+ return
+ end
+ if type(pwd.sudo) == "string" then
+ f:write(pwd.name .. " " .. pwd.sudo .. "\n")
+ elseif type(pwd.sudo) == "table" then
+ for _, str in ipairs(pwd.sudo) do
+ f:write(pwd.name .. " " .. str .. "\n")
+ end
+ end
+ f:close()
+ if chmodsudoers then
+ chmod(sudoers, "0440")
+ end
+ if chmodsudoersd then
+ chmod(sudoers_dir, "0750")
+ end
+end
+
+local function update_sshd_config(key, value)
+ local sshd_config = "/etc/ssh/sshd_config"
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ if root then
+ sshd_config = root .. sshd_config
+ end
+ local f = assert(io.open(sshd_config, "r+"))
+ local tgt = assert(io.open(sshd_config .. ".nuageinit", "w"))
+ local found = false
+ local pattern = "^%s*"..key:lower().."%s+(%w+)%s*#?.*$"
+ while true do
+ local line = f:read()
+ if line == nil then break end
+ local _, _, val = line:lower():find(pattern)
+ if val then
+ found = true
+ if val == value then
+ assert(tgt:write(line .. "\n"))
+ else
+ assert(tgt:write(key .. " " .. value .. "\n"))
+ end
+ else
+ assert(tgt:write(line .. "\n"))
+ end
+ end
+ if not found then
+ assert(tgt:write(key .. " " .. value .. "\n"))
+ end
+ assert(f:close())
+ assert(tgt:close())
+ os.rename(sshd_config .. ".nuageinit", sshd_config)
+end
+
+local function exec_change_password(user, password, type, expire)
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ local cmd = "pw "
+ if root then
+ cmd = cmd .. "-R " .. root .. " "
+ end
+ local postcmd = " -H 0"
+ local input = password
+ if type ~= nil and type == "text" then
+ postcmd = " -h 0"
+ else
+ if password == "RANDOM" then
+ input = nil
+ postcmd = " -w random"
+ end
+ end
+ cmd = cmd .. "usermod " .. user .. postcmd
+ if expire then
+ cmd = cmd .. " -p 1"
+ else
+ cmd = cmd .. " -p 0"
+ end
+ local f = io.popen(cmd .. " >/dev/null", "w")
+ if input then
+ f:write(input)
+ end
+ -- ignore stdout to avoid printing the password in case of random password
+ local r = f:close(cmd)
+ if not r then
+ warnmsg("fail to change user password ".. user)
+ warnmsg(cmd)
+ end
+end
+
+local function change_password_from_line(line, expire)
+ local user, password = line:match("%s*(%w+):(%S+)%s*")
+ local type = nil
+ if user and password then
+ if password == "R" then
+ password = "RANDOM"
+ end
+ if not password:match("^%$%d+%$%w+%$") then
+ if password ~= "RANDOM" then
+ type = "text"
+ end
+ end
+ exec_change_password(user, password, type, expire)
+ end
+end
+
+local function chpasswd(obj)
+ if type(obj) ~= "table" then
+ warnmsg("Invalid chpasswd entry, expecting an object")
+ return
+ end
+ local expire = false
+ if obj.expire ~= nil then
+ if type(obj.expire) == "boolean" then
+ expire = obj.expire
+ else
+ warnmsg("Invalid type for chpasswd.expire, expecting a boolean, got a ".. type(obj.expire))
+ end
+ end
+ if obj.users ~= nil then
+ if type(obj.users) ~= "table" then
+ warnmsg("Invalid type for chpasswd.users, expecting a list, got a ".. type(obj.users))
+ goto list
+ end
+ for _, u in ipairs(obj.users) do
+ if type(u) ~= "table" then
+ warnmsg("Invalid chpasswd.users entry, expecting an object, got a " .. type(u))
+ goto next
+ end
+ if not u.name then
+ warnmsg("Invalid entry for chpasswd.users: missing 'name'")
+ goto next
+ end
+ if not u.password then
+ warnmsg("Invalid entry for chpasswd.users: missing 'password'")
+ goto next
+ end
+ exec_change_password(u.name, u.password, u.type, expire)
+ ::next::
+ end
+ end
+ ::list::
+ if obj.list ~= nil then
+ warnmsg("chpasswd.list is deprecated consider using chpasswd.users")
+ if type(obj.list) == "string" then
+ for line in obj.list:gmatch("[^\n]+") do
+ change_password_from_line(line, expire)
+ end
+ elseif type(obj.list) == "table" then
+ for _, u in ipairs(obj.list) do
+ change_password_from_line(u, expire)
+ end
+ end
+ end
+end
+
+local function settimezone(timezone)
+ if timezone == nil then
+ return
+ end
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ if not root then
+ root = "/"
+ end
+
+ f, _, rc = os.execute("tzsetup -s -C " .. root .. " " .. timezone)
+
+ if not f then
+ warnmsg("Impossible to configure time zone ( rc = " .. rc .. " )")
+ return
+ end
+end
+
+local function pkg_bootstrap()
+ if os.getenv("NUAGE_RUN_TESTS") then
+ return true
+ end
+ if os.execute("pkg -N 2>/dev/null") then
+ return true
+ end
+ print("Bootstrapping pkg")
+ return os.execute("env ASSUME_ALWAYS_YES=YES pkg bootstrap")
+end
+
+local function install_package(package)
+ if package == nil then
+ return true
+ end
+ local install_cmd = "pkg install -y " .. package
+ local test_cmd = "pkg info -q " .. package
+ if os.getenv("NUAGE_RUN_TESTS") then
+ print(install_cmd)
+ print(test_cmd)
+ return true
+ end
+ if os.execute(test_cmd) then
+ return true
+ end
+ return os.execute(install_cmd)
+end
+
+local function run_pkg_cmd(subcmd)
+ local cmd = "env ASSUME_ALWAYS_YES=yes pkg " .. subcmd
+ if os.getenv("NUAGE_RUN_TESTS") then
+ print(cmd)
+ return true
+ end
+ return os.execute(cmd)
+end
+local function update_packages()
+ return run_pkg_cmd("update")
+end
+
+local function upgrade_packages()
+ return run_pkg_cmd("upgrade")
+end
+
+local function addfile(file, defer)
+ if type(file) ~= "table" then
+ return false, "Invalid object"
+ end
+ if defer and not file.defer then
+ return true
+ end
+ if not defer and file.defer then
+ return true
+ end
+ if not file.path then
+ return false, "No path provided for the file to write"
+ end
+ local content = nil
+ if file.content then
+ if file.encoding then
+ if file.encoding == "b64" or file.encoding == "base64" then
+ content = decode_base64(file.content)
+ else
+ return false, "Unsupported encoding: " .. file.encoding
+ end
+ else
+ content = file.content
+ end
+ end
+ local mode = "w"
+ if file.append then
+ mode = "a"
+ end
+
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ if not root then
+ root = ""
+ end
+ local filepath = root .. file.path
+ local f = assert(io.open(filepath, mode))
+ if content then
+ f:write(content)
+ end
+ f:close()
+ if file.permissions then
+ chmod(filepath, file.permissions)
+ end
+ if file.owner then
+ local owner, group = string.match(file.owner, "([^:]+):([^:]+)")
+ if not owner then
+ owner = file.owner
+ end
+ chown(filepath, owner, group)
+ end
+ return true
+end
+
+local n = {
+ warn = warnmsg,
+ err = errmsg,
+ chmod = chmod,
+ chown = chown,
+ dirname = dirname,
+ mkdir_p = mkdir_p,
+ sethostname = sethostname,
+ settimezone = settimezone,
+ adduser = adduser,
+ addgroup = addgroup,
+ addsshkey = addsshkey,
+ update_sshd_config = update_sshd_config,
+ chpasswd = chpasswd,
+ pkg_bootstrap = pkg_bootstrap,
+ install_package = install_package,
+ update_packages = update_packages,
+ upgrade_packages = upgrade_packages,
+ addsudo = addsudo,
+ adddoas = adddoas,
+ addfile = addfile
+}
+
+return n
diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit
new file mode 100755
index 000000000000..f29fa8ba1bac
--- /dev/null
+++ b/libexec/nuageinit/nuageinit
@@ -0,0 +1,720 @@
+#!/usr/libexec/flua
+---
+-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+--
+-- Copyright(c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org>
+-- Copyright(c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org>
+
+local nuage = require("nuage")
+local lfs = require("lfs")
+local ucl = require("ucl")
+local yaml = require("lyaml")
+
+if #arg ~= 2 then
+ nuage.err("Usage: " .. arg[0] .. " <cloud-init-directory> (<config-2> | <nocloud>)", false)
+end
+local ni_path = arg[1]
+local citype = arg[2]
+
+local default_user = {
+ name = "freebsd",
+ homedir = "/home/freebsd",
+ groups = "wheel",
+ gecos = "FreeBSD User",
+ shell = "/bin/sh",
+ plain_text_passwd = "freebsd"
+}
+
+local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+if not root then
+ root = ""
+end
+
+local function openat(dir, name)
+ local path_dir = root .. dir
+ local path_name = path_dir .. "/" .. name
+ nuage.mkdir_p(path_dir)
+ local f, err = io.open(path_name, "w")
+ if not f then
+ nuage.err("unable to open " .. path_name .. ": " .. err)
+ end
+ return f, path_name
+end
+local function open_ssh_key(name)
+ return openat("/etc/ssh", name)
+end
+
+local function open_config(name)
+ return openat("/etc/rc.conf.d", name)
+end
+
+local function open_resolv_conf()
+ return openat("/etc", "resolv.conf")
+end
+
+local function open_resolvconf_conf()
+ return openat("/etc", "resolvconf.conf")
+end
+
+local function get_ifaces_by_mac()
+ local parser = ucl.parser()
+ -- grab ifaces
+ local ns = io.popen("netstat -i --libxo json")
+ local netres = ns:read("*a")
+ ns:close()
+ local res, err = parser:parse_string(netres)
+ if not res then
+ nuage.warn("Error parsing netstat -i --libxo json outout: " .. err)
+ return nil
+ end
+ local ifaces = parser:get_object()
+ local myifaces = {}
+ for _, iface in pairs(ifaces["statistics"]["interface"]) do
+ if iface["network"]:match("<Link#%d>") then
+ local s = iface["address"]
+ myifaces[s:lower()] = iface["name"]
+ end
+ end
+ return myifaces
+end
+
+local function sethostname(obj)
+ -- always prefer fqdn if specified over hostname
+ if obj.fqdn then
+ nuage.sethostname(obj.fqdn)
+ elseif obj.hostname then
+ nuage.sethostname(obj.hostname)
+ end
+end
+
+local function settimezone(obj)
+ nuage.settimezone(obj.timezone)
+end
+
+local function groups(obj)
+ if obj.groups == nil then return end
+
+ for n, g in pairs(obj.groups) do
+ if (type(g) == "string") then
+ local r = nuage.addgroup({name = g})
+ if not r then
+ nuage.warn("failed to add group: " .. g)
+ end
+ elseif type(g) == "table" then
+ for k, v in pairs(g) do
+ nuage.addgroup({name = k, members = v})
+ end
+ else
+ nuage.warn("invalid type: " .. type(g) .. " for users entry number " .. n)
+ end
+ end
+end
+
+local function create_default_user(obj)
+ if not obj.users then
+ -- default user if none are defined
+ nuage.adduser(default_user)
+ end
+end
+
+local function users(obj)
+ if obj.users == nil then return end
+
+ for n, u in pairs(obj.users) do
+ if type(u) == "string" then
+ if u == "default" then
+ nuage.adduser(default_user)
+ else
+ nuage.adduser({name = u})
+ end
+ elseif type(u) == "table" then
+ -- ignore users without a username
+ if u.name == nil then
+ goto unext
+ end
+ local homedir = nuage.adduser(u)
+ if u.ssh_authorized_keys then
+ for _, v in ipairs(u.ssh_authorized_keys) do
+ nuage.addsshkey(homedir, v)
+ end
+ end
+ if u.sudo then
+ nuage.addsudo(u)
+ end
+ if u.doas then
+ nuage.adddoas(u)
+ end
+ else
+ nuage.warn("invalid type : " .. type(u) .. " for users entry number " .. n)
+ end
+ ::unext::
+ end
+end
+
+local function ssh_keys(obj)
+ if obj.ssh_keys == nil then return end
+ if type(obj.ssh_keys) ~= "table" then
+ nuage.warn("Invalid type for ssh_keys")
+ return
+ end
+
+ for key, val in pairs(obj.ssh_keys) do
+ for keyname, keytype in key:gmatch("(%w+)_(%w+)") do
+ local sshkn = nil
+ if keytype == "public" then
+ sshkn = "ssh_host_" .. keyname .. "_key.pub"
+ elseif keytype == "private" then
+ sshkn = "ssh_host_" .. keyname .. "_key"
+ end
+ if sshkn then
+ local sshkey, path = open_ssh_key(sshkn)
+ if sshkey then
+ sshkey:write(val .. "\n")
+ sshkey:close()
+ end
+ if keytype == "private" then
+ nuage.chmod(path, "0600")
+ end
+ end
+ end
+ end
+end
+
+local function ssh_authorized_keys(obj)
+ if obj.ssh_authorized_keys == nil then return end
+ local homedir = nuage.adduser(default_user)
+ for _, k in ipairs(obj.ssh_authorized_keys) do
+ nuage.addsshkey(homedir, k)
+ end
+end
+
+local function nameservers(interface, obj)
+ local resolvconf_conf_handler = open_resolvconf_conf()
+
+ if obj.search then
+ local with_space = false
+
+ resolvconf_conf_handler:write('search_domains="')
+
+ for _, d in ipairs(obj.search) do
+ if with_space then
+ resolvconf_conf_handler:write(" " .. d)
+ else
+ resolvconf_conf_handler:write(d)
+ with_space = true
+ end
+ end
+
+ resolvconf_conf_handler:write('"\n')
+ end
+
+ if obj.addresses then
+ local with_space = false
+
+ resolvconf_conf_handler:write('name_servers="')
+
+ for _, a in ipairs(obj.addresses) do
+ if with_space then
+ resolvconf_conf_handler:write(" " .. a)
+ else
+ resolvconf_conf_handler:write(a)
+ with_space = true
+ end
+ end
+
+ resolvconf_conf_handler:write('"\n')
+ end
+
+ resolvconf_conf_handler:close()
+
+ local resolv_conf = root .. "/etc/resolv.conf"
+
+ resolv_conf_attr = lfs.attributes(resolv_conf)
+
+ if resolv_conf_attr == nil then
+ resolv_conf_handler = open_resolv_conf()
+ resolv_conf_handler:close()
+ end
+
+ if not os.execute("resolvconf -a " .. interface .. " < " .. resolv_conf) then
+ nuage.warn("Failed to execute resolvconf(8)")
+ end
+end
+
+local function install_packages(packages)
+ if not nuage.pkg_bootstrap() then
+ nuage.warn("Failed to bootstrap pkg, skip installing packages")
+ return
+ end
+ for n, p in pairs(packages) do
+ if type(p) == "string" then
+ if not nuage.install_package(p) then
+ nuage.warn("Failed to install : " .. p)
+ end
+ else
+ nuage.warn("Invalid type: " .. type(p) .. " for packages entry number " .. n)
+ end
+ end
+end
+
+local function list_ifaces()
+ local proc = io.popen("ifconfig -l")
+ local raw_ifaces = proc:read("*a")
+ proc:close()
+ local ifaces = {}
+ for i in raw_ifaces:gmatch("[^%s]+") do
+ table.insert(ifaces, i)
+ end
+ return ifaces
+end
+
+local function get_ifaces_by_driver()
+ local proc = io.popen("ifconfig -D")
+ local drivers = {}
+ local last_interface = nil
+ for line in proc:lines() do
+ local interface = line:match("^([%S]+): ")
+
+ if interface then
+ last_interface = interface
+ end
+
+ local driver = line:match("^[%s]+drivername: ([%S]+)$")
+
+ if driver then
+ drivers[driver] = last_interface
+ end
+ end
+ proc:close()
+
+ return drivers
+end
+
+local function match_rules(rules)
+ -- To comply with the cloud-init specification, all rules must match and a table
+ -- with the matching interfaces must be returned. This changes the way we initially
+ -- thought about our implementation, since at first we only needed one interface,
+ -- but cloud-init performs actions on a group of matching interfaces.
+ local interfaces = {}
+ if rules.macaddress then
+ local ifaces = get_ifaces_by_mac()
+ local interface = ifaces[rules.macaddress]
+ if not interface then
+ nuage.warn("not interface matching by MAC address: " .. rules.macaddress)
+ return
+ end
+ interfaces[interface] = 1
+ end
+ if rules.name then
+ local match = false
+ for _, i in pairs(list_ifaces()) do
+ if i:match(rules.name) then
+ match = true
+ interfaces[i] = 1
+ end
+ end
+ if not match then
+ nuage.warn("not interface matching by name: " .. rules.name)
+ return
+ end
+ end
+ if rules.driver then
+ local match = false
+ local drivers = get_ifaces_by_driver()
+ for d in pairs(drivers) do
+ if d:match(rules.driver) then
+ match = true
+ interface = drivers[d]
+ interfaces[interface] = 1
+ end
+ end
+ if not match then
+ nuage.warn("not interface matching by driver: " .. rules.driver)
+ return
+ end
+ end
+ return interfaces
+end
+
+local function write_files(files, defer)
+ if not files then
+ return
+ end
+ for n, file in pairs(files) do
+ local r, errstr = nuage.addfile(file, defer)
+ if not r then
+ nuage.warn("Skipping write_files entry number " .. n .. ": " .. errstr)
+ end
+ end
+end
+
+local function write_files_not_defered(obj)
+ write_files(obj.write_files, false)
+end
+
+local function write_files_defered(obj)
+ write_files(obj.write_files, true)
+end
+-- Set network configuration from user_data
+local function network_config(obj)
+ if obj.network == nil then return end
+
+ local network = open_config("network")
+ local routing = open_config("routing")
+ local ipv6 = {}
+ local set_defaultrouter = true
+ local set_defaultrouter6 = true
+ local set_nameservers = true
+ for i, v in pairs(obj.network.ethernets) do
+ local interfaces = {}
+ if v.match then
+ interfaces = match_rules(v.match)
+
+ if next(interfaces) == nil then
+ goto next
+ end
+ else
+ interfaces[i] = 1
+ end
+ local extra_opts = ""
+ if v.wakeonlan then
+ extra_opts = extra_opts .. " wol"
+ end
+ if v.mtu then
+ if type(v.mtu) == "number" then
+ mtu = tostring(v.mtu)
+ else
+ mtu = v.mtu
+ end
+ if mtu:match("%d") then
+ extra_opts = extra_opts .. " mtu " .. mtu
+ else
+ nuage.warn("MTU is not set because the specified value is invalid: " .. mtu)
+ end
+ end
+ for interface in pairs(interfaces) do
+ if v.match and v.match.macaddress and v["set-name"] then
+ local ifaces = get_ifaces_by_mac()
+ local matched = ifaces[v.match.macaddress]
+ if matched and matched == interface then
+ network:write("ifconfig_" .. interface .. '_name=' .. v["set-name"] .. '\n')
+ interface = v["set-name"]
+ end
+ end
+ if v.dhcp4 then
+ network:write("ifconfig_" .. interface .. '="DHCP"' .. extra_opts .. '\n')
+ elseif v.addresses then
+ for _, a in pairs(v.addresses) do
+ if a:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)") then
+ network:write("ifconfig_" .. interface .. '="inet ' .. a .. extra_opts .. '"\n')
+ else
+ network:write("ifconfig_" .. interface .. '_ipv6="inet6 ' .. a .. extra_opts .. '"\n')
+ ipv6[#ipv6 + 1] = interface
+ end
+ end
+ if set_nameservers and v.nameservers then
+ set_nameservers = false
+ nameservers(interface, v.nameservers)
+ end
+ if set_defaultrouter and v.gateway4 then
+ set_defaultrouter = false
+ routing:write('defaultrouter="' .. v.gateway4 .. '"\n')
+ end
+ if v.gateway6 then
+ if set_defaultrouter6 then
+ set_defaultrouter6 = false
+ routing:write('ipv6_defaultrouter="' .. v.gateway6 .. '"\n')
+ end
+ routing:write("ipv6_route_" .. interface .. '="' .. v.gateway6)
+ routing:write(" -prefixlen 128 -interface " .. interface .. '"\n')
+ end
+ end
+ end
+ ::next::
+ end
+ if #ipv6 > 0 then
+ network:write('ipv6_network_interfaces="')
+ network:write(table.concat(ipv6, " ") .. '"\n')
+ network:write('ipv6_default_interface="' .. ipv6[1] .. '"\n')
+ end
+ network:close()
+ routing:close()
+end
+
+local function ssh_pwauth(obj)
+ if obj.ssh_pwauth == nil then return end
+
+ local value = "no"
+ if obj.ssh_pwauth then
+ value = "yes"
+ end
+ nuage.update_sshd_config("PasswordAuthentication", value)
+end
+
+local function runcmd(obj)
+ if obj.runcmd == nil then return end
+ local f = nil
+ for _, c in ipairs(obj.runcmd) do
+ if f == nil then
+ nuage.mkdir_p(root .. "/var/cache/nuageinit")
+ f = assert(io.open(root .. "/var/cache/nuageinit/runcmds", "w"))
+ f:write("#!/bin/sh\n")
+ end
+ f:write(c .. "\n")
+ end
+ if f ~= nil then
+ f:close()
+ nuage.chmod(root .. "/var/cache/nuageinit/runcmds", "0755")
+ end
+end
+
+local function packages(obj)
+ if obj.package_update then
+ nuage.update_packages()
+ end
+ if obj.package_upgrade then
+ nuage.upgrade_packages()
+ end
+ if obj.packages then
+ install_packages(obj.packages)
+ end
+end
+
+local function chpasswd(obj)
+ if obj.chpasswd == nil then return end
+ nuage.chpasswd(obj.chpasswd)
+end
+
+local function config2_network(p)
+ local parser = ucl.parser()
+ local f = io.open(p .. "/network_data.json")
+ if not f then
+ -- silently return no network configuration is provided
+ return
+ end
+ f:close()
+ local res, err = parser:parse_file(p .. "/network_data.json")
+ if not res then
+ nuage.warn("error parsing network_data.json: " .. err)
+ return
+ end
+ local obj = parser:get_object()
+
+ local ifaces = get_ifaces_by_mac()
+ if not ifaces then
+ nuage.warn("no network interfaces found")
+ return
+ end
+ local mylinks = {}
+ for _, v in pairs(obj["links"]) do
+ local s = v["ethernet_mac_address"]:lower()
+ mylinks[v["id"]] = ifaces[s]
+ end
+
+ local network = open_config("network")
+ local routing = open_config("routing")
+ local ipv6 = {}
+ local ipv6_routes = {}
+ local ipv4 = {}
+ for _, v in pairs(obj["networks"]) do
+ local interface = mylinks[v["link"]]
+ if v["type"] == "ipv4_dhcp" then
+ network:write("ifconfig_" .. interface .. '="DHCP"\n')
+ end
+ if v["type"] == "ipv4" then
+ network:write(
+ "ifconfig_" .. interface .. '="inet ' .. v["ip_address"] .. " netmask " .. v["netmask"] .. '"\n'
+ )
+ if v["gateway"] then
+ routing:write('defaultrouter="' .. v["gateway"] .. '"\n')
+ end
+ if v["routes"] then
+ for i, r in ipairs(v["routes"]) do
+ local rname = "cloudinit" .. i .. "_" .. interface
+ if v["gateway"] and v["gateway"] == r["gateway"] then
+ goto next
+ end
+ if r["network"] == "0.0.0.0" then
+ routing:write('defaultrouter="' .. r["gateway"] .. '"\n')
+ goto next
+ end
+ routing:write("route_" .. rname .. '="-net ' .. r["network"] .. " ")
+ routing:write(r["gateway"] .. " " .. r["netmask"] .. '"\n')
+ ipv4[#ipv4 + 1] = rname
+ ::next::
+ end
+ end
+ end
+ if v["type"] == "ipv6" then
+ ipv6[#ipv6 + 1] = interface
+ ipv6_routes[#ipv6_routes + 1] = interface
+ network:write("ifconfig_" .. interface .. '_ipv6="inet6 ' .. v["ip_address"] .. '"\n')
+ if v["gateway"] then
+ routing:write('ipv6_defaultrouter="' .. v["gateway"] .. '"\n')
+ routing:write("ipv6_route_" .. interface .. '="' .. v["gateway"])
+ routing:write(" -prefixlen 128 -interface " .. interface .. '"\n')
+ end
+ -- TODO compute the prefixlen for the routes
+ --if v["routes"] then
+ -- for i, r in ipairs(v["routes"]) do
+ -- local rname = "cloudinit" .. i .. "_" .. mylinks[v["link"]]
+ -- -- skip all the routes which are already covered by the default gateway, some provider
+ -- -- still list plenty of them.
+ -- if v["gateway"] == r["gateway"] then
+ -- goto next
+ -- end
+ -- routing:write("ipv6_route_" .. rname .. '"\n')
+ -- ipv6_routes[#ipv6_routes + 1] = rname
+ -- ::next::
+ -- end
+ --end
+ end
+ end
+ if #ipv4 > 0 then
+ routing:write('static_routes="')
+ routing:write(table.concat(ipv4, " ") .. '"\n')
+ end
+ if #ipv6 > 0 then
+ network:write('ipv6_network_interfaces="')
+ network:write(table.concat(ipv6, " ") .. '"\n')
+ network:write('ipv6_default_interface="' .. ipv6[1] .. '"\n')
+ end
+ if #ipv6_routes > 0 then
+ routing:write('ipv6_static_routes="')
+ routing:write(table.concat(ipv6, " ") .. '"\n')
+ end
+ network:close()
+ routing:close()
+end
+
+local function parse_network_config()
+ local nc_file = ni_path .. "/network-config"
+ local nc_file_attr = lfs.attributes(nc_file)
+ if nc_file_attr == nil then
+ return
+ end
+ local f, err = io.open(nc_file)
+ if err then
+ nuage.err("error parsing nocloud network-config: " .. err)
+ end
+ local obj = yaml.load(f:read("*a"))
+ f:close()
+ if not obj then
+ nuage.err("error parsing nocloud network-config")
+ end
+ local netobj = {}
+ netobj["network"] = obj
+ return netobj
+end
+
+if citype == "config-2" then
+ local parser = ucl.parser()
+ local res, err = parser:parse_file(ni_path .. "/meta_data.json")
+
+ if not res then
+ nuage.err("error parsing config-2 meta_data.json: " .. err)
+ end
+ local obj = parser:get_object()
+ if obj.public_keys then
+ local homedir = nuage.adduser(default_user)
+ for _,v in pairs(obj.public_keys) do
+ nuage.addsshkey(homedir, v)
+ end
+ end
+ nuage.sethostname(obj["hostname"])
+
+ -- network
+ config2_network(ni_path)
+elseif citype == "nocloud" then
+ local f, err = io.open(ni_path .. "/meta-data")
+ if err then
+ nuage.err("error parsing nocloud meta-data: " .. err)
+ end
+ local obj = yaml.load(f:read("*a"))
+ f:close()
+ if not obj then
+ nuage.err("error parsing nocloud meta-data")
+ end
+ local hostname = obj["local-hostname"]
+ if not hostname then
+ hostname = obj["hostname"]
+ end
+ if hostname then
+ nuage.sethostname(hostname)
+ end
+elseif citype ~= "postnet" then
+ nuage.err("Unknown cloud init type: " .. citype)
+end
+
+-- deal with user-data
+local ud = nil
+local f = nil
+local userdatas = {"user-data", "user_data"}
+for _, v in pairs(userdatas) do
+ f = io.open(ni_path .. "/" .. v, "r")
+ if f then
+ ud = v
+ break
+ end
+end
+if not f then
+ os.exit(0)
+end
+local line = f:read("*l")
+if citype ~= "postnet" then
+ local content = f:read("*a")
+ nuage.mkdir_p(root .. "/var/cache/nuageinit")
+ local tof = assert(io.open(root .. "/var/cache/nuageinit/user_data", "w"))
+ tof:write(line .. "\n" .. content)
+ tof:close()
+end
+f:close()
+if line == "#cloud-config" then
+ local pre_network_calls = {
+ sethostname,
+ settimezone,
+ groups,
+ create_default_user,
+ ssh_keys,
+ ssh_authorized_keys,
+ network_config,
+ ssh_pwauth,
+ runcmd,
+ write_files_not_defered,
+ }
+
+ local post_network_calls = {
+ packages,
+ users,
+ chpasswd,
+ write_files_defered,
+ }
+
+ f = io.open(ni_path .. "/" .. ud)
+ local obj = yaml.load(f:read("*a"))
+ f:close()
+ if not obj then
+ nuage.err("error parsing cloud-config file: " .. ud)
+ end
+
+ local calls_table = pre_network_calls
+ if citype == "postnet" then
+ calls_table = post_network_calls
+ end
+
+ for i = 1, #calls_table do
+ if citype == "nocloud" and calls_table[i] == network_config then
+ netobj = parse_network_config()
+ if netobj == nil then
+ network_config(obj)
+ else
+ network_config(netobj)
+ end
+ else
+ calls_table[i](obj)
+ end
+ end
+elseif line:sub(1, 2) == "#!" then
+ -- delay for execution at rc.local time --
+ nuage.chmod(root .. "/var/cache/nuageinit/user_data", "0755")
+end
diff --git a/libexec/nuageinit/nuageinit.7 b/libexec/nuageinit/nuageinit.7
new file mode 100644
index 000000000000..b527c984970c
--- /dev/null
+++ b/libexec/nuageinit/nuageinit.7
@@ -0,0 +1,431 @@
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2025 Baptiste Daroussin <bapt@FreeBSD.org>
+.\" Copyright (c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org>
+.\"
+.Dd June 26, 2025
+.Dt NUAGEINIT 7
+.Os
+.Sh NAME
+.Nm nuageinit
+.Nd initialize a cloud-init environment
+.Sh DESCRIPTION
+The
+.Nm
+program is used to initialize instances in a cloud environment.
+.Nm
+runs at the first boot after the system installation.
+It is composed of three
+.Xr rc 8
+scripts:
+.Bl -tag -width "nuageinit"
+.It Cm nuageinit
+This script detects the type of cloud environment and gathers
+the configuration data accordingly.
+The following cloud environments are supported right now:
+.Bl -tag -width "OpenStack"
+.It ondisk
+A cloud agnostic environment where the disk is provided to the system
+with the configuration data on it.
+The disk must be formatted using one of the following filesystems:
+.Xr cd9660 4
+or
+.Xr msdosfs 4
+and be labelled (via filesystem label) either
+.Ar config-2
+or
+.Ar cidata .
+.It OpenStack
+The system is running in an
+.Lk https://www.openstack.org/ OpenStack environment .
+It is detected via the
+.Ar smbios.system.product
+.Xr smbios 4
+description available in
+.Xr kenv 2 .
+.El
+.Pp
+Depending on the cloud environment above,
+.Nm
+will attempt to configure the instance.
+This script executes early
+after all the local filesystem are mounted but before
+the network is configured.
+.It Cm nuageinit_post_net
+This script is responsible for processing the configurations that are network
+dependent:
+.Bl -bullet
+.It
+dealing with packages
+.It
+dealing with users (which can depend on shell provided by packages)
+.El
+.It Cm nuageinit_user_data_script
+This script is responsible for executing everything which would have
+been passed via the configuration to be executed, via the configuration
+or because the user_data provided is a script.
+.El
+.Pp
+The default user for nuageinit is a user named
+.Va freebsd
+with a password set to
+.Va freebsd
+and a login shell set to
+.Va /bin/sh .
+.Sh CONFIGURATION
+The configuration of
+.Nm
+is typically provided as metadata by the cloud provider.
+The metadata is presented to nuageinit in different forms depending on
+the provider:
+.Bl -tag -width "config-2"
+.It nocloud
+If the data is provided via a disk labelled
+.Va cidata ,
+then the metadata is provided in the form of a file named
+.Pa meta-data
+in YAML format.
+.Nm
+will configure the hostname of the instance according to the value of the
+following variables
+.Va local-hostname
+or
+.Va hostname .
+.It config-2
+If the data is provided via a disk labelled
+.Va config-2
+or if it is fetched from OpenStack,
+the metadata is expected in two json files:
+.Pp
+The
+.Pa meta_data.json
+file supports the following keys:
+.Bl -tag -width "public_keys"
+.It Ic hostname
+Set the hostname of the instance.
+.It Ic public_keys
+Append each entry of the array to
+.Nm
+default user which will be created.
+.El
+.Pp
+The
+.Pa network_data.json
+file supports the following keys:
+.Bl -tag -width "public_keys"
+.It Ic links
+Array of network interfaces to be configured.
+.It Ic networks
+Array of network configurations to be set.
+.El
+.El
+.Pp
+Along with the metadata, a user data file is provided, either named
+.Pa user_data
+or
+.Pa user-data .
+If this file starts with a
+.Qq #! ,
+it will be executed at the end of the boot via
+.Cm nuageinit_user_data_script .
+If this file starts with
+.Qq #!cloud-config ,
+it will be parsed as a YAML configuration file.
+All other cases will be ignored.
+.Pp
+The
+.Qq #!cloud-config
+configuration entries supported by
+.Nm :
+.Bl -tag -width "config-2"
+.It Ic fqdn
+Specify a fully qualified domain name for the instance.
+.It Ic hostname
+Specify the hostname of the instance if
+.Qq Ic fqdn
+is not set.
+.It Ic timezone
+Sets the system timezone based on the value provided.
+.Pp
+See also
+.Xr tzfile 3 Ns .
+.It Ic groups
+An array of strings or objects to be created:
+.Bl -bullet
+.It
+If the entry is a string,
+a group using this string as a name will be created.
+.It
+if the entry is an object, the
+.Qq Ar key
+will be used as the name of the group, the
+.Qq Ar value
+is expected to be a list of members (array), specified by name.
+.El
+.It Ic ssh_keys
+An object of multiple key/values,
+.Qq Cm keys
+being in the form
+.Ar algo_private
+or
+.Ar algo_public ,
+.Qq Cm values
+being the actual content of the files in
+.Pa /etc/ssh .
+.It Ic ssh_authorized_keys
+Append each entry of the array to
+.Nm
+default user which will be created.
+.It Ic ssh_pwauth
+boolean which determines the value of the
+.Qq Ic PasswordAuthentication
+configuration in
+.Pa /etc/ssh/sshd_config
+.It Ic network
+Network configuration parameters.
+.Pp
+Specifying the following parameters from a file named
+.Pa network-config
+takes precedence over their specification from the
+.Ic network
+parameter of
+.Pa user-data Ns .
+.Bl -tag -width "ethernets"
+.It Ic ethernets
+Mapping representing a generic configuration for existing network interfaces.
+.Pp
+Each key is an interface name that is only used when no
+.Sy match
+rule is specified.
+If
+.Sy match
+rules are specified, an arbitrary name can be used
+.Po e.g.: id0 Pc Ns .
+.Bl -tag -width "nameservers"
+.It Ic match
+This selects a subset of available physical devices by various hardware properties.
+The following configuration will then apply to all matching devices, as soon as
+they appear.
+All specified properties must match.
+The following properties for
+creating matches are supported:
+.Bl -tag -width "macaddress"
+.It Ic macaddress
+.No Device's MAC address in the form Sy xx:xx:xx:xx:xx:xx Ns .
+Letters should be lowercase.
+.It Ic name
+Current interface name.
+Lua pattern-matching expressions are supported.
+.It Ic driver
+Interface driver name and unit number of the interface.
+Lua pattern-natching expressions
+are supported.
+.El
+.It Ic set-name
+When matching on unique properties such as MAC, match rules can be written so that they
+match only one device.
+Then this property can be used to give that device a more
+specific/desirable/nicer name than the default.
+.Pp
+While multiple properties can be used in a match,
+.Sy macaddress
+is required for nuageinit to perform the rename.
+.It Ic mtu
+The MTU key represents a device's Maximum Transmission Unit, the largest size packet
+or frame.
+.It Ic wakeonlan
+Enable wake on LAN.
+Off by default.
+.It Ic dhcp4
+Configure the interface to use DHCP.
+.Pp
+This takes precedence over
+.Sy addresses
+when both are specified.
+.It Ic addresses
+List of strings representing IPv4 or IPv6 addresses.
+.It Ic gateway4
+Set default gateway for IPv4, for manual address configuration.
+This requires setting
+.Sy addresses
+too.
+.Pp
+Since only one default router can be configured at a time, this parameter is applied
+when processing the first entry, and any others are silently ignored.
+.It Ic gateway6
+Set default gateway for IPv6, for manual address configuration.
+This requires setting
+.Sy addresses
+too.
+.Pp
+Since only one default router can be configured at a time, this parameter is applied
+when processing the first entry, and any others are silently ignored.
+.It Ic nameservers
+Set DNS servers and search domains, for manual address configuration.
+.Pp
+There are two supported fields:
+.Bl -tag -width "addresses"
+.It Ic search
+Search list for host-name lookup.
+.It Ic addresses
+List of IPv4 or IPv6 name server addresses that the resolver should query.
+.El
+.El
+.El
+.It Ic runcmd
+An array of commands to be run at the end of the boot process
+.It Ic packages
+List of packages to be installed.
+.It Ic package_update
+Update the remote package metadata.
+.It Ic package_upgrade
+Upgrade the packages installed to their latest version.
+.It Ic users
+Specify a list of users to be created:
+.Bl -tag -width "ssh_authorized_keys"
+.It Ic name
+Name of the user.
+.It Ic gecos
+GECOS for the user.
+.It Ic homedir
+The path of the home directory for the user.
+.It Ic primary_group
+The main group the user should belong to.
+.It Ic groups
+The list of other groups the user should belong to.
+.It Ic no_create_home
+A boolean which determines if the home directory should be created or not.
+.It Ic shell
+The shell that should be used for the user.
+.It Ic ssh_authorized_keys
+List of SSH keys for the user.
+.It Ic passwd
+The encrypted password for the user.
+.It Ic plain_text_passwd
+The password in plain text for the user.
+Ignored if an encrypted password is already provided.
+.It Ic locked
+Boolean to determine if the user account should be locked.
+.It Ic sudo
+A string or an array of strings which should be appended to
+.Pa ${LOCALBASE}/etc/sudoers.d/90-nuageinit-users
+.It Ic doas
+A string or an array of strings which should be appended to
+.Pa ${LOCALBASE}/etc/doas.conf
+.Pp
+Instead of hardcoding the username, you can use
+.Sy %u Ns ,
+which will be replaced by the current username.
+.El
+.Pp
+A special case exist: if the entry is a simple string with the value
+.Qq default ,
+then the default user is created.
+.It Ic chpasswd
+Change the passwords for users, it accepts the following keys:
+.Bl -tag -width "expire"
+.It Ic expire
+Boolean to force the user to change their password on first login.
+.It Ic users
+An array of objects:
+.Bl -tag -width "password"
+.It Ic user
+Specify the user whose password will be changed.
+.It Ic password
+Specify a text line with the new password or
+specify the user whose password will be changed.
+.Qq Cm RANDOM
+to assign the password randomly.
+If the textline starts with
+.Qq Cm $x$
+where x is a number, then the password is considered encrypted,
+otherwise the password is considered plaintext.
+.El
+.El
+.It Ic write_files
+An array of objects representing files to be created at first boot.
+The files are being created before the installation of any packages
+and the creation of the users.
+The only mandatory field is:
+.Ic path .
+It accepts the following keys for each objects:
+.Bl -tag -width "permissions"
+.It Ic content
+The content to be written to the file.
+If this key is not existing then an empty file will be created.
+.It Ic encoding
+Specify the encoding used for content.
+If not specified, then plain text is considered.
+Only
+.Ar b64
+and
+.Ar base64
+are supported for now.
+.It Ic path
+The path of the file to be created.
+.Pq Note intermerdiary directories will not be created .
+.It Ic permissions
+A string representing the permission of the file in octal.
+.It Ic owner
+A string representing the owner, two forms are possible:
+.Ar user
+or
+.Ar user:group .
+.It Ic append
+A boolean to specify the content should be appended to the file if the file
+exists.
+.It Ic defer
+A boolean to specify that the files should be created after the packages are
+installed and the users are created.
+.El
+.El
+.Sh EXAMPLES
+Here is an example of a YAML configuration for
+.Nm :
+.Bd -literal
+#cloud-config
+fqdn: myhost.mynetwork.tld
+users:
+ - default
+ - name: user
+ gecos: Foo B. Bar
+ sudo: ALL=(ALL) NOPASSWD:ALL
+ ssh_authorized_keys:
+ - ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAr...
+packages:
+ - neovim
+ - git-lite
+package_update: true
+package_upgrade: true
+runcmd:
+ - logger -t nuageinit "boot finished"
+ssh_keys:
+ ed25519_private: |
+ -----BEGIN OPENSSH PRIVATE KEY-----
+ blabla
+ ...
+ -----END OPENSSH PRIVATE KEY-----
+ ed25519_public: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK+MH4E8KO32N5CXRvXVqvyZVl0+6ue4DobdhU0FqFd+
+network:
+ ethernets:
+ vtnet0:
+ addresses:
+ - 192.168.8.2/24
+ gateway4: 192.168.8.1
+.Ed
+.Sh SEE ALSO
+.Xr kenv 2 ,
+.Xr cd9660 4 ,
+.Xr msdosfs 4 ,
+.Xr smbios 4 ,
+.Xr ssh_config 5 ,
+.Xr rc 8
+.Sh STANDARDS
+.Nm
+is believed to conform to the
+.Lk https://cloud-init.io/ Cloud Init
+specification.
+.Sh HISTORY
+.Nm
+appeared in
+.Fx 14.1
diff --git a/libexec/nuageinit/tests/Makefile b/libexec/nuageinit/tests/Makefile
new file mode 100644
index 000000000000..dc8997717b59
--- /dev/null
+++ b/libexec/nuageinit/tests/Makefile
@@ -0,0 +1,22 @@
+PACKAGE= tests
+.PATH: ${SRCTOP}/usr.sbin/pw/tests
+
+BINDIR= ${TESTSDIR}
+
+PROGS= crypt
+LIBADD= crypt
+
+ATF_TESTS_SH= nuage utils nuageinit
+
+${PACKAGE}FILES+= addgroup.lua
+${PACKAGE}FILES+= addsshkey.lua
+${PACKAGE}FILES+= adduser.lua
+${PACKAGE}FILES+= adduser_passwd.lua
+${PACKAGE}FILES+= dirname.lua
+${PACKAGE}FILES+= err.lua
+${PACKAGE}FILES+= sethostname.lua
+${PACKAGE}FILES+= settimezone.lua
+${PACKAGE}FILES+= warn.lua
+${PACKAGE}FILES+= addfile.lua
+
+.include <bsd.test.mk>
diff --git a/libexec/nuageinit/tests/Makefile.depend b/libexec/nuageinit/tests/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/nuageinit/tests/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/nuageinit/tests/addfile.lua b/libexec/nuageinit/tests/addfile.lua
new file mode 100644
index 000000000000..98d020e557c0
--- /dev/null
+++ b/libexec/nuageinit/tests/addfile.lua
@@ -0,0 +1,71 @@
+#!/bin/libexec/flua
+
+local n = require("nuage")
+local lfs = require("lfs")
+
+local f = {
+ content = "plop"
+}
+
+local r, err = n.addfile(f, false)
+if r or err ~= "No path provided for the file to write" then
+ n.err("addfile should not accept a file to write without a path")
+end
+
+local function addfile_and_getres(file)
+ local r, err = n.addfile(file, false)
+ if not r then
+ n.err(err)
+ end
+ local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+ if not root then
+ root = ""
+ end
+ local filepath = root .. file.path
+ local resf = assert(io.open(filepath, "r"))
+ local str = resf:read("*all")
+ resf:close()
+ return str
+end
+
+-- simple file
+f.path="/tmp/testnuage"
+local str = addfile_and_getres(f)
+if str ~= f.content then
+ n.err("Invalid file content")
+end
+
+-- the file is overwriten
+f.content = "test"
+
+str = addfile_and_getres(f)
+if str ~= f.content then
+ n.err("Invalid file content, not overwritten")
+end
+
+-- try to append now
+f.content = "more"
+f.append = true
+
+str = addfile_and_getres(f)
+if str ~= "test" .. f.content then
+ n.err("Invalid file content, not appended")
+end
+
+-- base64
+f.content = "YmxhCg=="
+f.encoding = "base64"
+f.append = false
+
+str = addfile_and_getres(f)
+if str ~= "bla\n" then
+ n.err("Invalid file content, base64 decode")
+end
+
+-- b64
+f.encoding = "b64"
+str = addfile_and_getres(f)
+if str ~= "bla\n" then
+ n.err("Invalid file content, b64 decode")
+ print("==>" .. str .. "<==")
+end
diff --git a/libexec/nuageinit/tests/addgroup.lua b/libexec/nuageinit/tests/addgroup.lua
new file mode 100644
index 000000000000..a36a5e24c7b3
--- /dev/null
+++ b/libexec/nuageinit/tests/addgroup.lua
@@ -0,0 +1,16 @@
+#!/usr/libexec/flua
+
+local n = require("nuage")
+
+if n.addgroup() then
+ n.err("addgroup should not accept empty value")
+end
+if n.addgroup("plop") then
+ n.err("addgroup should not accept empty value")
+end
+local gr = {}
+gr.name = "impossible_groupname"
+local res = n.addgroup(gr)
+if not res then
+ n.err("valid addgroup should return a path")
+end
diff --git a/libexec/nuageinit/tests/addsshkey.lua b/libexec/nuageinit/tests/addsshkey.lua
new file mode 100644
index 000000000000..47e102c162a9
--- /dev/null
+++ b/libexec/nuageinit/tests/addsshkey.lua
@@ -0,0 +1,5 @@
+#!/usr/libexec/flua
+
+local n = require("nuage")
+
+n.addsshkey(".", "mykey")
diff --git a/libexec/nuageinit/tests/adduser.lua b/libexec/nuageinit/tests/adduser.lua
new file mode 100644
index 000000000000..cef6be0c0e0c
--- /dev/null
+++ b/libexec/nuageinit/tests/adduser.lua
@@ -0,0 +1,16 @@
+#!/usr/libexec/flua
+
+local n = require("nuage")
+
+if n.adduser() then
+ n.err("adduser should not accept empty value")
+end
+if n.adduser("plop") then
+ n.err("adduser should not accept empty value")
+end
+local pw = {}
+pw.name = "impossible_username"
+local res = n.adduser(pw)
+if not res then
+ n.err("valid adduser should return a path")
+end
diff --git a/libexec/nuageinit/tests/adduser_passwd.lua b/libexec/nuageinit/tests/adduser_passwd.lua
new file mode 100644
index 000000000000..e2d9395d679d
--- /dev/null
+++ b/libexec/nuageinit/tests/adduser_passwd.lua
@@ -0,0 +1,20 @@
+#!/usr/libexec/flua
+
+local n = require("nuage")
+
+local pw = {}
+pw.name = "foo"
+pw.plain_text_passwd = "bar"
+local res = n.adduser(pw)
+if not res then
+ n.err("valid user should return a path")
+end
+
+local pw2 = {}
+pw2.name = "foocrypted"
+-- barcrypted
+pw2.passwd = "$6$ZY8faYcEfyoEZnNX$FuAZA2SKhIfYLebhEtbmjptQNrenr6mJhji35Ru.zqdaa6G/gkKiHoQuh0vYZTKrjaykyohR8W4Q5ZF56yt8u1"
+res = n.adduser(pw2)
+if not res then
+ n.err("valid user should return a path")
+end
diff --git a/libexec/nuageinit/tests/dirname.lua b/libexec/nuageinit/tests/dirname.lua
new file mode 100644
index 000000000000..7e3a2c835502
--- /dev/null
+++ b/libexec/nuageinit/tests/dirname.lua
@@ -0,0 +1,11 @@
+#!/usr/libexec/flua
+
+local n = require("nuage")
+
+print(n.dirname("/my/path/path1"))
+if n.dirname("path") then
+ n.err('Expecting nil for n.dirname("path")')
+end
+if n.dirname() then
+ n.err("Expecting nil for n.dirname")
+end
diff --git a/libexec/nuageinit/tests/err.lua b/libexec/nuageinit/tests/err.lua
new file mode 100644
index 000000000000..567d4f2df66e
--- /dev/null
+++ b/libexec/nuageinit/tests/err.lua
@@ -0,0 +1,5 @@
+#!/usr/libexec/flua
+
+local n = require("nuage")
+
+n.err("plop")
diff --git a/libexec/nuageinit/tests/nuage.sh b/libexec/nuageinit/tests/nuage.sh
new file mode 100644
index 000000000000..57d83b62928a
--- /dev/null
+++ b/libexec/nuageinit/tests/nuage.sh
@@ -0,0 +1,101 @@
+#-
+# Copyright (c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org>
+# Copyright (c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+export NUAGE_FAKE_ROOTDIR="$PWD"
+
+atf_test_case sethostname
+atf_test_case settimezone
+atf_test_case addsshkey
+atf_test_case adduser
+atf_test_case adduser_passwd
+atf_test_case addgroup
+atf_test_case addfile
+
+settimezone_body()
+{
+ atf_check /usr/libexec/flua $(atf_get_srcdir)/settimezone.lua
+ if [ ! -f etc/localtime ]; then
+ atf_fail "localtime not written"
+ fi
+}
+
+sethostname_body()
+{
+ atf_check /usr/libexec/flua $(atf_get_srcdir)/sethostname.lua
+ if [ ! -f etc/rc.conf.d/hostname ]; then
+ atf_fail "hostname not written"
+ fi
+ atf_check -o inline:"hostname=\"myhostname\"\n" cat etc/rc.conf.d/hostname
+}
+
+addsshkey_body()
+{
+ atf_check /usr/libexec/flua $(atf_get_srcdir)/addsshkey.lua
+ if [ ! -f .ssh/authorized_keys ]; then
+ atf_fail "ssh key not added"
+ fi
+ atf_check -o inline:"40700\n" stat -f %p .ssh
+ atf_check -o inline:"100600\n" stat -f %p .ssh/authorized_keys
+ atf_check -o inline:"mykey\n" cat .ssh/authorized_keys
+ atf_check /usr/libexec/flua $(atf_get_srcdir)/addsshkey.lua
+ atf_check -o inline:"mykey\nmykey\n" cat .ssh/authorized_keys
+}
+
+adduser_head()
+{
+ atf_set "require.user" root
+}
+adduser_body()
+{
+ mkdir etc
+ printf "root:*:0:0::0:0:Charlie &:/root:/bin/sh\n" > etc/master.passwd
+ pwd_mkdb -d etc etc/master.passwd
+ printf "wheel:*:0:root\n" > etc/group
+ atf_check -e inline:"nuageinit: Argument should be a table\nnuageinit: Argument should be a table\n" /usr/libexec/flua $(atf_get_srcdir)/adduser.lua
+ test -d home/impossible_username || atf_fail "home not created"
+ atf_check -o inline:"impossible_username::1001:1001::0:0:impossible_username User:/home/impossible_username:/bin/sh\n" grep impossible_username etc/master.passwd
+}
+
+adduser_passwd_body()
+{
+ mkdir etc
+ printf "root:*:0:0::0:0:Charlie &:/root:/bin/sh\n" > etc/master.passwd
+ pwd_mkdb -d etc etc/master.passwd
+ printf "wheel:*:0:root\n" > etc/group
+ atf_check /usr/libexec/flua $(atf_get_srcdir)/adduser_passwd.lua
+ test -d home/foo || atf_fail "home not created"
+ passhash=`awk -F ':' '/^foo:/ {print $2}' etc/master.passwd`
+ atf_check -s exit:0 -o inline:$passhash \
+ $(atf_get_srcdir)/crypt $passhash "bar"
+ passhash=`awk -F ':' '/^foocrypted:/ {print $2}' etc/master.passwd`
+ atf_check -s exit:0 -o inline:$passhash \
+ $(atf_get_srcdir)/crypt $passhash "barcrypted"
+}
+
+addgroup_body()
+{
+ mkdir etc
+ printf "wheel:*:0:root\n" > etc/group
+ atf_check -e inline:"nuageinit: Argument should be a table\nnuageinit: Argument should be a table\n" /usr/libexec/flua $(atf_get_srcdir)/addgroup.lua
+ atf_check -o inline:"impossible_groupname:*:1001:\n" grep impossible_groupname etc/group
+}
+
+addfile_body()
+{
+ mkdir tmp
+ atf_check /usr/libexec/flua $(atf_get_srcdir)/addfile.lua
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case sethostname
+ atf_add_test_case addsshkey
+ atf_add_test_case adduser
+ atf_add_test_case adduser_passwd
+ atf_add_test_case addgroup
+ atf_add_test_case addfile
+}
diff --git a/libexec/nuageinit/tests/nuageinit.sh b/libexec/nuageinit/tests/nuageinit.sh
new file mode 100644
index 000000000000..2b7c5226c97a
--- /dev/null
+++ b/libexec/nuageinit/tests/nuageinit.sh
@@ -0,0 +1,947 @@
+#-
+# Copyright (c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org>
+# Copyright (c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+export NUAGE_FAKE_ROOTDIR="$PWD"
+
+atf_test_case args
+atf_test_case nocloud
+atf_test_case nocloud_userdata_script
+atf_test_case nocloud_user_data_script
+atf_test_case nocloud_userdata_cloudconfig_users
+atf_test_case nocloud_network
+atf_test_case config2
+atf_test_case config2_pubkeys
+atf_test_case config2_pubkeys_user_data
+atf_test_case config2_pubkeys_meta_data
+atf_test_case config2_network
+atf_test_case config2_network_static_v4
+atf_test_case config2_ssh_keys
+atf_test_case nocloud_userdata_cloudconfig_ssh_pwauth
+atf_test_case nocloud_userdata_cloudconfig_chpasswd
+atf_test_case nocloud_userdata_cloudconfig_chpasswd_list_string
+atf_test_case nocloud_userdata_cloudconfig_chpasswd_list_list
+atf_test_case config2_userdata_runcmd
+atf_test_case config2_userdata_packages
+atf_test_case config2_userdata_update_packages
+atf_test_case config2_userdata_upgrade_packages
+atf_test_case config2_userdata_shebang
+atf_test_case config2_userdata_fqdn_and_hostname
+atf_test_case config2_userdata_write_files
+
+setup_test_adduser()
+{
+ here=$(pwd)
+ export NUAGE_FAKE_ROOTDIR=$(pwd)
+ mkdir -p etc/ssh
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/csh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/csh
+EOF
+ pwd_mkdb -d etc ${here}/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+}
+
+args_body()
+{
+ atf_check -s exit:1 -e inline:"Usage: /usr/libexec/nuageinit <cloud-init-directory> (<config-2> | <nocloud>)\n" /usr/libexec/nuageinit
+ atf_check -s exit:1 -e inline:"Usage: /usr/libexec/nuageinit <cloud-init-directory> (<config-2> | <nocloud>)\n" /usr/libexec/nuageinit bla
+ atf_check -s exit:1 -e inline:"Usage: /usr/libexec/nuageinit <cloud-init-directory> (<config-2> | <nocloud>)\n" /usr/libexec/nuageinit bla meh plop
+ atf_check -s exit:1 -e inline:"nuageinit: Unknown cloud init type: meh\n" /usr/libexec/nuageinit bla meh
+}
+
+nocloud_body()
+{
+ mkdir -p media/nuageinit
+ atf_check -s exit:1 -e match:"nuageinit: error parsing nocloud.*" /usr/libexec/nuageinit "${PWD}"/media/nuageinit/ nocloud
+ printf "instance-id: iid-local01\nlocal-hostname: cloudimg\n" > "${PWD}"/media/nuageinit/meta-data
+ atf_check -s exit:0 /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o inline:"hostname=\"cloudimg\"\n" cat etc/rc.conf.d/hostname
+ cat > media/nuageinit/meta-data << EOF
+instance-id: iid-local01
+hostname: myhost
+EOF
+ atf_check -s exit:0 /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o inline:"hostname=\"myhost\"\n" cat etc/rc.conf.d/hostname
+}
+
+nocloud_userdata_script_body()
+{
+ mkdir -p media/nuageinit
+ printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data
+ printf "#!/bin/sh\necho yeah\n" > "${PWD}"/media/nuageinit/user-data
+ chmod 755 "${PWD}"/media/nuageinit/user-data
+ atf_check -s exit:0 /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o inline:"#!/bin/sh\necho yeah\n" cat var/cache/nuageinit/user_data
+}
+
+nocloud_user_data_script_body()
+{
+ mkdir -p media/nuageinit
+ printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data
+ printf "#!/bin/sh\necho yeah\n" > "${PWD}"/media/nuageinit/user_data
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check -s exit:0 /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o inline:"#!/bin/sh\necho yeah\n" cat var/cache/nuageinit/user_data
+}
+
+nocloud_userdata_cloudconfig_users_head()
+{
+ atf_set "require.user" root
+}
+nocloud_userdata_cloudconfig_users_body()
+{
+ mkdir -p media/nuageinit
+ printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data
+ mkdir -p etc
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/sh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
+EOF
+ pwd_mkdb -d etc "${PWD}"/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+groups:
+ - admingroup: [root,sys]
+ - cloud-users
+users:
+ - default
+ - name: foobar
+ gecos: Foo B. Bar
+ primary_group: foobar
+ sudo: ALL=(ALL) NOPASSWD:ALL
+ doas: permit persist %u as root
+ groups: users
+ passwd: $6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/
+ - name: bla
+ sudo:
+ - "ALL=(ALL) NOPASSWD:/usr/sbin/pw"
+ - "ALL=(ALL) ALL"
+ doas:
+ - "deny %u as foobar"
+ - "permit persist %u as root cmd whoami"
+EOF
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ cat > expectedgroup << EOF
+wheel:*:0:root,freebsd
+users:*:1:foobar
+admingroup:*:1001:root,sys
+cloud-users:*:1002:
+freebsd:*:1003:
+foobar:*:1004:
+bla:*:1005:
+EOF
+ cat > expectedpasswd << 'EOF'
+root:*:0:0::0:0:Charlie &:/root:/bin/sh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
+freebsd:freebsd:1001:1003::0:0:FreeBSD User:/home/freebsd:/bin/sh
+foobar:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/:1002:1004::0:0:Foo B. Bar:/home/foobar:/bin/sh
+bla::1003:1005::0:0:bla User:/home/bla:/bin/sh
+EOF
+ sed -i "" "s/freebsd:.*:1001/freebsd:freebsd:1001/" "${PWD}"/etc/master.passwd
+ atf_check -o file:expectedpasswd cat "${PWD}"/etc/master.passwd
+ atf_check -o file:expectedgroup cat "${PWD}"/etc/group
+ localbase=`sysctl -ni user.localbase 2> /dev/null`
+ if [ -z "${localbase}" ]; then
+ # fallback
+ localbase="/usr/local"
+ fi
+ atf_check -o inline:"foobar ALL=(ALL) NOPASSWD:ALL\nbla ALL=(ALL) NOPASSWD:/usr/sbin/pw\nbla ALL=(ALL) ALL\n" cat "${PWD}/${localbase}/etc/sudoers.d/90-nuageinit-users"
+ atf_check -o inline:"permit persist foobar as root\ndeny bla as foobar\npermit persist bla as root cmd whoami\n" cat "${PWD}/${localbase}/etc/doas.conf"
+}
+
+nocloud_network_head()
+{
+ atf_set "require.user" root
+}
+nocloud_network_body()
+{
+ mkdir -p media/nuageinit
+ mkdir -p etc
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/sh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
+EOF
+ pwd_mkdb -d etc "${PWD}"/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ mynetworks=$(ifconfig -l ether)
+ if [ -z "$mynetworks" ]; then
+ atf_skip "a network interface is needed"
+ fi
+ set -- $mynetworks
+ myiface=$1
+ myaddr=$(ifconfig $myiface ether | awk '/ether/ { print $2 }')
+ printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data
+ cat > media/nuageinit/user-data << EOF
+#cloud-config
+network:
+ version: 2
+ ethernets:
+ # opaque ID for physical interfaces, only referred to by other stanzas
+ id0:
+ match:
+ macaddress: "$myaddr"
+ addresses:
+ - 192.0.2.2/24
+ - 2001:db8::2/64
+ gateway4: 192.0.2.1
+ gateway6: 2001:db8::1
+EOF
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ cat > network << EOF
+ifconfig_${myiface}="inet 192.0.2.2/24"
+ifconfig_${myiface}_ipv6="inet6 2001:db8::2/64"
+ipv6_network_interfaces="${myiface}"
+ipv6_default_interface="${myiface}"
+EOF
+ cat > routing << EOF
+defaultrouter="192.0.2.1"
+ipv6_defaultrouter="2001:db8::1"
+ipv6_route_${myiface}="2001:db8::1 -prefixlen 128 -interface ${myiface}"
+EOF
+ atf_check -o file:network cat "${PWD}"/etc/rc.conf.d/network
+ atf_check -o file:routing cat "${PWD}"/etc/rc.conf.d/routing
+}
+
+config2_body()
+{
+ mkdir -p media/nuageinit
+ atf_check -s exit:1 -e match:"nuageinit: error parsing config-2 meta_data.json:.*" /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ printf "{}" > media/nuageinit/meta_data.json
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ cat > media/nuageinit/meta_data.json << EOF
+{
+ "hostname": "cloudimg"
+}
+EOF
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ atf_check -o inline:"hostname=\"cloudimg\"\n" cat etc/rc.conf.d/hostname
+}
+
+config2_pubkeys_head()
+{
+ atf_set "require.user" root
+}
+config2_pubkeys_body()
+{
+ mkdir -p media/nuageinit
+ touch media/nuageinit/meta_data.json
+ cat > media/nuageinit/user-data << EOF
+#cloud-config
+ssh_authorized_keys:
+ - "ssh-rsa AAAAB3NzaC1y...== Generated by Nova"
+EOF
+ mkdir -p etc
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/sh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
+EOF
+ pwd_mkdb -d etc "${PWD}"/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ atf_check -o inline:"ssh-rsa AAAAB3NzaC1y...== Generated by Nova\n" cat home/freebsd/.ssh/authorized_keys
+}
+
+config2_pubkeys_user_data_head()
+{
+ atf_set "require.user" root
+}
+config2_pubkeys_user_data_body()
+{
+ mkdir -p media/nuageinit
+ touch media/nuageinit/meta_data.json
+ cat > media/nuageinit/user_data << EOF
+#cloud-config
+ssh_authorized_keys:
+ - "ssh-rsa AAAAB3NzaC1y...== Generated by Nova"
+EOF
+ mkdir -p etc
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/sh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
+EOF
+ pwd_mkdb -d etc "${PWD}"/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ atf_check -o inline:"ssh-rsa AAAAB3NzaC1y...== Generated by Nova\n" cat home/freebsd/.ssh/authorized_keys
+}
+
+config2_pubkeys_meta_data_body()
+{
+ here=$(pwd)
+ export NUAGE_FAKE_ROOTDIR=$(pwd)
+ if [ $(id -u) -ne 0 ]; then
+ atf_skip "root required"
+ fi
+ mkdir -p media/nuageinit
+ cat > media/nuageinit/meta_data.json << EOF
+{
+ "uuid": "uuid_for_this_instance",
+ "admin_pass": "a_generated_password",
+ "public_keys": {
+ "tdb": "ssh-ed25519 my_key_id tdb@host"
+ },
+ "keys": [
+ {
+ "name": "tdb",
+ "type": "ssh",
+ "data": "ssh-ed25519 my_key_id tdb@host"
+ }
+ ],
+ "hostname": "freebsd-14-test.novalocal",
+ "name": "freebsd-14-test",
+ "launch_index": 0,
+ "availability_zone": "nova",
+ "random_seed": "long_random_seed",
+ "project_id": "my_project_id",
+ "devices": [],
+ "dedicated_cpus": []
+}
+EOF
+ mkdir -p etc
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/csh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/csh
+EOF
+ pwd_mkdb -d etc ${here}/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ atf_check /usr/libexec/nuageinit ${here}/media/nuageinit config-2
+ atf_check -o inline:"ssh-ed25519 my_key_id tdb@host\n" cat home/freebsd/.ssh/authorized_keys
+}
+
+config2_network_body()
+{
+ mkdir -p media/nuageinit
+ printf "{}" > media/nuageinit/meta_data.json
+ mynetworks=$(ifconfig -l ether)
+ if [ -z "$mynetworks" ]; then
+ atf_skip "a network interface is needed"
+ fi
+ set -- $mynetworks
+ myiface=$1
+ myaddr=$(ifconfig $myiface ether | awk '/ether/ { print $2 }')
+cat > media/nuageinit/network_data.json << EOF
+{
+ "links": [
+ {
+ "ethernet_mac_address": "$myaddr",
+ "id": "iface0",
+ "mtu": null
+ }
+ ],
+ "networks": [
+ {
+ "id": "network0",
+ "link": "iface0",
+ "type": "ipv4_dhcp"
+ },
+ { // IPv6
+ "id": "private-ipv4",
+ "type": "ipv6",
+ "link": "iface0",
+ // supports condensed IPv6 with CIDR netmask
+ "ip_address": "2001:db8::3257:9652/64",
+ "gateway": "fd00::1",
+ "routes": [
+ {
+ "network": "::",
+ "netmask": "::",
+ "gateway": "fd00::1"
+ },
+ {
+ "network": "::",
+ "netmask": "ffff:ffff:ffff::",
+ "gateway": "fd00::1:1"
+ }
+ ],
+ "network_id": "da5bb487-5193-4a65-a3df-4a0055a8c0d8"
+ }
+ ]
+}
+EOF
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ cat > network << EOF
+ifconfig_${myiface}="DHCP"
+ifconfig_${myiface}_ipv6="inet6 2001:db8::3257:9652/64"
+ipv6_network_interfaces="${myiface}"
+ipv6_default_interface="${myiface}"
+EOF
+ cat > routing << EOF
+ipv6_defaultrouter="fd00::1"
+ipv6_route_${myiface}="fd00::1 -prefixlen 128 -interface ${myiface}"
+ipv6_static_routes="${myiface}"
+EOF
+ atf_check -o file:network cat "${PWD}"/etc/rc.conf.d/network
+ atf_check -o file:routing cat "${PWD}"/etc/rc.conf.d/routing
+}
+
+config2_network_static_v4_body()
+{
+ mkdir -p media/nuageinit
+ printf "{}" > media/nuageinit/meta_data.json
+ mynetworks=$(ifconfig -l ether)
+ if [ -z "$mynetworks" ]; then
+ atf_skip "a network interface is needed"
+ fi
+ set -- $mynetworks
+ myiface=$1
+ myaddr=$(ifconfig $myiface ether | awk '/ether/ { print $2 }')
+cat > media/nuageinit/network_data.json << EOF
+{
+ "links": [
+ {
+ "ethernet_mac_address": "$myaddr",
+ "id": "iface0",
+ "mtu": null
+ }
+ ],
+ "networks": [
+ {
+ "id": "network0",
+ "link": "iface0",
+ "type": "ipv4",
+ "ip_address": "10.184.0.244",
+ "netmask": "255.255.240.0",
+ "routes": [
+ {
+ "network": "10.0.0.0",
+ "netmask": "255.0.0.0",
+ "gateway": "11.0.0.1"
+ },
+ {
+ "network": "0.0.0.0",
+ "netmask": "0.0.0.0",
+ "gateway": "23.253.157.1"
+ }
+ ]
+ }
+ ]
+}
+EOF
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ cat > network << EOF
+ifconfig_${myiface}="inet 10.184.0.244 netmask 255.255.240.0"
+EOF
+ cat > routing << EOF
+route_cloudinit1_${myiface}="-net 10.0.0.0 11.0.0.1 255.0.0.0"
+defaultrouter="23.253.157.1"
+static_routes="cloudinit1_${myiface}"
+EOF
+ atf_check -o file:network cat "${PWD}"/etc/rc.conf.d/network
+ atf_check -o file:routing cat "${PWD}"/etc/rc.conf.d/routing
+}
+
+config2_ssh_keys_head()
+{
+ atf_set "require.user" root
+}
+config2_ssh_keys_body()
+{
+ here=$(pwd)
+ export NUAGE_FAKE_ROOTDIR=$(pwd)
+ mkdir -p media/nuageinit
+ touch media/nuageinit/meta_data.json
+ cat > media/nuageinit/user-data << EOF
+#cloud-config
+ssh_keys:
+ rsa_private: |
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIBxwIBAAJhAKD0YSHy73nUgysO13XsJmd4fHiFyQ+00R7VVu2iV9Qco
+ ...
+ -----END RSA PRIVATE KEY-----
+ rsa_public: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEAoPRhIfLvedSDKw7Xd ...
+ ed25519_private: |
+ -----BEGIN OPENSSH PRIVATE KEY-----
+ blabla
+ ...
+ -----END OPENSSH PRIVATE KEY-----
+ ed25519_public: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK+MH4E8KO32N5CXRvXVqvyZVl0+6ue4DobdhU0FqFd+
+EOF
+ mkdir -p etc/ssh
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/csh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/csh
+EOF
+ pwd_mkdb -d etc ${here}/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ _expected="-----BEGIN RSA PRIVATE KEY-----
+MIIBxwIBAAJhAKD0YSHy73nUgysO13XsJmd4fHiFyQ+00R7VVu2iV9Qco
+...
+-----END RSA PRIVATE KEY-----
+
+"
+ atf_check -o inline:"${_expected}" cat ${PWD}/etc/ssh/ssh_host_rsa_key
+ _expected="ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEAoPRhIfLvedSDKw7Xd ...\n"
+ atf_check -o inline:"${_expected}" cat ${PWD}/etc/ssh/ssh_host_rsa_key.pub
+ _expected="-----BEGIN OPENSSH PRIVATE KEY-----
+blabla
+...
+-----END OPENSSH PRIVATE KEY-----
+
+"
+ atf_check -o inline:"${_expected}" cat ${PWD}/etc/ssh/ssh_host_ed25519_key
+ _expected="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK+MH4E8KO32N5CXRvXVqvyZVl0+6ue4DobdhU0FqFd+\n"
+ atf_check -o inline:"${_expected}" cat ${PWD}/etc/ssh/ssh_host_ed25519_key.pub
+}
+
+
+nocloud_userdata_cloudconfig_ssh_pwauth_head()
+{
+ atf_set "require.user" root
+}
+nocloud_userdata_cloudconfig_ssh_pwauth_body()
+{
+ mkdir -p etc
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/sh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
+EOF
+ pwd_mkdb -d etc "${PWD}"/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ mkdir -p media/nuageinit
+ printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+ssh_pwauth: true
+EOF
+ mkdir -p etc/ssh/
+ touch etc/ssh/sshd_config
+
+ atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o inline:"PasswordAuthentication yes\n" cat etc/ssh/sshd_config
+
+ # Same value we don't touch anything
+ printf " PasswordAuthentication yes # I want password\n" > etc/ssh/sshd_config
+ atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o inline:" PasswordAuthentication yes # I want password\n" cat etc/ssh/sshd_config
+
+ printf " PasswordAuthentication no # Should change\n" > etc/ssh/sshd_config
+ atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o inline:"PasswordAuthentication yes\n" cat etc/ssh/sshd_config
+
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+ssh_pwauth: false
+EOF
+
+ printf " PasswordAuthentication no # no passwords\n" > etc/ssh/sshd_config
+ atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o inline:" PasswordAuthentication no # no passwords\n" cat etc/ssh/sshd_config
+
+ printf " PasswordAuthentication yes # Should change\n" > etc/ssh/sshd_config
+ atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o inline:"PasswordAuthentication no\n" cat etc/ssh/sshd_config
+}
+
+nocloud_userdata_cloudconfig_chpasswd_head()
+{
+ atf_set "require.user" root
+}
+nocloud_userdata_cloudconfig_chpasswd_body()
+{
+ mkdir -p etc
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/sh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
+user:*:1:0::0:0:Sys:/home/sys:/bin/sh
+EOF
+ pwd_mkdb -d etc "${PWD}"/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ mkdir -p media/nuageinit
+ printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+chpasswd:
+ expire: true
+ users:
+ - { user: "sys", password: RANDOM }
+EOF
+
+ atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o empty -e inline:"nuageinit: Invalid entry for chpasswd.users: missing 'name'\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ # nothing modified
+ atf_check -o inline:"sys:*:1:0::0:0:Sys:/home/sys:/bin/sh\n" pw -R $(pwd) usershow sys
+
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+chpasswd:
+ expire: true
+ users:
+ - { name: "sys", pwd: RANDOM }
+EOF
+ atf_check -o empty -e inline:"nuageinit: Invalid entry for chpasswd.users: missing 'password'\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ # nothing modified
+ atf_check -o inline:"sys:*:1:0::0:0:Sys:/home/sys:/bin/sh\n" pw -R $(pwd) usershow sys
+
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+chpasswd:
+ expire: false
+ users:
+ - { name: "sys", password: RANDOM }
+EOF
+ # not empty because the password is printed to stdout
+ atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ atf_check -o match:'sys:\$.*:1:0::0:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys
+
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+chpasswd:
+ expire: true
+ users:
+ - { name: "sys", password: RANDOM }
+EOF
+ # not empty because the password is printed to stdout
+ atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ atf_check -o match:'sys:\$.*:1:0::1:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys
+
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+chpasswd:
+ expire: true
+ users:
+ - { name: "user", password: "$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/" }
+EOF
+ # not empty because the password is printed to stdout
+ atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ atf_check -o inline:'user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/:1:0::1:0:Sys:/home/sys:/bin/sh\n' pw -R $(pwd) usershow user
+}
+
+
+nocloud_userdata_cloudconfig_chpasswd_list_string_head()
+{
+ atf_set "require.user" root
+}
+nocloud_userdata_cloudconfig_chpasswd_list_string_body()
+{
+ mkdir -p etc
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/sh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
+user:*:1:0::0:0:Sys:/home/sys:/bin/sh
+EOF
+ pwd_mkdb -d etc "${PWD}"/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ mkdir -p media/nuageinit
+ printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+chpasswd:
+ expire: true
+ list: |
+ sys:RANDOM
+EOF
+
+ atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o empty -e inline:"nuageinit: chpasswd.list is deprecated consider using chpasswd.users\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ atf_check -o match:'sys:\$.*:1:0::1:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys
+
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+chpasswd:
+ expire: false
+ list: |
+ sys:plop
+ user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/
+ root:R
+EOF
+
+ atf_check -o empty -e ignore /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ atf_check -o match:'sys:\$.*:1:0::0:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys
+ atf_check -o inline:'user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/:1:0::0:0:Sys:/home/sys:/bin/sh\n' pw -R $(pwd) usershow user
+ atf_check -o match:'root:\$.*:0:0::0:0:Charlie &:/root:/bin/sh$' pw -R $(pwd) usershow root
+}
+
+nocloud_userdata_cloudconfig_chpasswd_list_list_head()
+{
+ atf_set "require.user" root
+}
+nocloud_userdata_cloudconfig_chpasswd_list_list_body()
+{
+ mkdir -p etc
+ cat > etc/master.passwd << EOF
+root:*:0:0::0:0:Charlie &:/root:/bin/sh
+sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
+user:*:1:0::0:0:Sys:/home/sys:/bin/sh
+EOF
+ pwd_mkdb -d etc "${PWD}"/etc/master.passwd
+ cat > etc/group << EOF
+wheel:*:0:root
+users:*:1:
+EOF
+ mkdir -p media/nuageinit
+ printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+chpasswd:
+ expire: true
+ list:
+ - sys:RANDOM
+EOF
+
+ atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
+ atf_check -o empty -e inline:"nuageinit: chpasswd.list is deprecated consider using chpasswd.users\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ atf_check -o match:'sys:\$.*:1:0::1:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys
+
+ cat > media/nuageinit/user-data << 'EOF'
+#cloud-config
+chpasswd:
+ expire: false
+ list:
+ - sys:plop
+ - user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/
+ - root:R
+EOF
+
+ atf_check -o empty -e ignore /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ atf_check -o match:'sys:\$.*:1:0::0:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys
+ atf_check -o inline:'user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/:1:0::0:0:Sys:/home/sys:/bin/sh\n' pw -R $(pwd) usershow user
+ atf_check -o match:'root:\$.*:0:0::0:0:Charlie &:/root:/bin/sh$' pw -R $(pwd) usershow root
+}
+
+config2_userdata_runcmd_head()
+{
+ atf_set "require.user" root
+}
+config2_userdata_runcmd_body()
+{
+ mkdir -p media/nuageinit
+ setup_test_adduser
+ printf "{}" > media/nuageinit/meta_data.json
+ cat > media/nuageinit/user_data << 'EOF'
+#cloud-config
+runcmd:
+EOF
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ cat > media/nuageinit/user_data << 'EOF'
+#cloud-config
+runcmd:
+ - plop
+EOF
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check -s exit:0 /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ test -f var/cache/nuageinit/runcmds || atf_fail "File not created"
+ test -x var/cache/nuageinit/runcmds || atf_fail "Missing execution permission"
+ atf_check -o inline:"#!/bin/sh\nplop\n" cat var/cache/nuageinit/runcmds
+
+ cat > media/nuageinit/user_data << 'EOF'
+#cloud-config
+runcmd:
+ - echo "yeah!"
+ - uname -s
+EOF
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ atf_check -o inline:"#!/bin/sh\necho \"yeah!\"\nuname -s\n" cat var/cache/nuageinit/runcmds
+}
+
+config2_userdata_packages_head()
+{
+ atf_set "require.user" root
+}
+
+config2_userdata_packages_body()
+{
+ mkdir -p media/nuageinit
+ setup_test_adduser
+ export NUAGE_RUN_TESTS=1
+ printf "{}" > media/nuageinit/meta_data.json
+ cat > media/nuageinit/user_data << 'EOF'
+#cloud-config
+packages:
+EOF
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ cat > media/nuageinit/user_data << 'EOF'
+#cloud-config
+packages:
+ - yeah/plop
+EOF
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check -s exit:0 -o inline:"pkg install -y yeah/plop\npkg info -q yeah/plop\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+
+ cat > media/nuageinit/user_data << 'EOF'
+#cloud-config
+packages:
+ - curl
+EOF
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check -o inline:"pkg install -y curl\npkg info -q curl\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+
+ cat > media/nuageinit/user_data << 'EOF'
+#cloud-config
+packages:
+ - curl
+ - meh: bla
+EOF
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check -o inline:"pkg install -y curl\npkg info -q curl\n" -e inline:"nuageinit: Invalid type: table for packages entry number 2\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+}
+
+config2_userdata_update_packages_body()
+{
+ mkdir -p media/nuageinit
+ setup_test_adduser
+ export NUAGE_RUN_TESTS=1
+ printf "{}" > media/nuageinit/meta_data.json
+ cat > media/nuageinit/user_data << 'EOF'
+#cloud-config
+package_update: true
+EOF
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check -o inline:"env ASSUME_ALWAYS_YES=yes pkg update\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+}
+
+config2_userdata_upgrade_packages_body()
+{
+ mkdir -p media/nuageinit
+ setup_test_adduser
+ export NUAGE_RUN_TESTS=1
+ printf "{}" > media/nuageinit/meta_data.json
+ cat > media/nuageinit/user_data << 'EOF'
+#cloud-config
+package_upgrade: true
+EOF
+ chmod 755 "${PWD}"/media/nuageinit/user_data
+ atf_check -o inline:"env ASSUME_ALWAYS_YES=yes pkg upgrade\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+}
+
+config2_userdata_shebang_body()
+{
+ mkdir -p media/nuageinit
+ setup_test_adduser
+ printf "{}" > media/nuageinit/meta_data.json
+ cat > media/nuageinit/user_data <<EOF
+#!/we/dont/care
+anything
+EOF
+ atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ test -f var/cache/nuageinit/user_data || atf_fail "File not created"
+ test -x var/cache/nuageinit/user_data || atf_fail "Missing execution permission"
+ atf_check -o inline:"#!/we/dont/care\nanything\n" cat var/cache/nuageinit/user_data
+ cat > media/nuageinit/user_data <<EOF
+/we/dont/care
+EOF
+ rm var/cache/nuageinit/user_data
+ if [ -f var/cache/nuageinit/user_data ]; then
+ atf_fail "File should not have been created"
+ fi
+}
+
+config2_userdata_write_files_body()
+{
+ mkdir -p media/nuageinit
+ setup_test_adduser
+ printf "{}" > media/nuageinit/meta_data.json
+ cat > media/nuageinit/user_data <<EOF
+#cloud-config
+write_files:
+- content: "plop"
+ path: /file1
+- path: /emptyfile
+- content: !!binary |
+ YmxhCg==
+ path: /file_base64
+ encoding: b64
+ permissions: '0755'
+ owner: nobody
+- content: "bob"
+ path: "/foo"
+ defer: true
+EOF
+ atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ atf_check -o inline:"plop" cat file1
+ atf_check -o inline:"" cat emptyfile
+ atf_check -o inline:"bla\n" cat file_base64
+ test -f foo && atf_fail "foo creation should have been defered"
+ atf_check -o match:"^-rwxr-xr-x.*nobody" ls -l file_base64
+ rm file1 emptyfile file_base64
+ atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+ test -f file1 -o -f emptyfile -o -f file_base64 && atf_fail "defer not working properly"
+ atf_check -o inline:"bob" cat foo
+}
+
+config2_userdata_fqdn_and_hostname_body()
+{
+ mkdir -p media/nuageinit
+ setup_test_adduser
+ printf "{}" > media/nuageinit/meta_data.json
+ cat > media/nuageinit/user_data <<EOF
+#cloud-config
+fqdn: host.domain.tld
+hostname: host
+EOF
+ atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ atf_check -o inline:"hostname=\"host.domain.tld\"\n" cat ${PWD}/etc/rc.conf.d/hostname
+ cat > media/nuageinit/user_data <<EOF
+#cloud-config
+hostname: host
+EOF
+ atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+ atf_check -o inline:"hostname=\"host\"\n" cat ${PWD}/etc/rc.conf.d/hostname
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case args
+ atf_add_test_case nocloud
+ atf_add_test_case nocloud_userdata_script
+ atf_add_test_case nocloud_user_data_script
+ atf_add_test_case nocloud_userdata_cloudconfig_users
+ atf_add_test_case nocloud_network
+ atf_add_test_case config2
+ atf_add_test_case config2_pubkeys
+ atf_add_test_case config2_pubkeys_user_data
+ atf_add_test_case config2_pubkeys_meta_data
+ atf_add_test_case config2_network
+ atf_add_test_case config2_network_static_v4
+ atf_add_test_case config2_ssh_keys
+ atf_add_test_case nocloud_userdata_cloudconfig_ssh_pwauth
+ atf_add_test_case nocloud_userdata_cloudconfig_chpasswd
+ atf_add_test_case nocloud_userdata_cloudconfig_chpasswd_list_string
+ atf_add_test_case nocloud_userdata_cloudconfig_chpasswd_list_list
+ atf_add_test_case config2_userdata_runcmd
+ atf_add_test_case config2_userdata_packages
+ atf_add_test_case config2_userdata_update_packages
+ atf_add_test_case config2_userdata_upgrade_packages
+ atf_add_test_case config2_userdata_shebang
+ atf_add_test_case config2_userdata_fqdn_and_hostname
+ atf_add_test_case config2_userdata_write_files
+}
diff --git a/libexec/nuageinit/tests/sethostname.lua b/libexec/nuageinit/tests/sethostname.lua
new file mode 100644
index 000000000000..47632497b545
--- /dev/null
+++ b/libexec/nuageinit/tests/sethostname.lua
@@ -0,0 +1,5 @@
+#!/usr/libexec/flua
+
+local n = require("nuage")
+
+n.sethostname("myhostname")
diff --git a/libexec/nuageinit/tests/settimezone.lua b/libexec/nuageinit/tests/settimezone.lua
new file mode 100644
index 000000000000..a8cacf09f4e7
--- /dev/null
+++ b/libexec/nuageinit/tests/settimezone.lua
@@ -0,0 +1,5 @@
+#!/usr/libexec/flua
+
+local n = require("nuage")
+
+n.settimezone("UTC")
diff --git a/libexec/nuageinit/tests/utils.sh b/libexec/nuageinit/tests/utils.sh
new file mode 100644
index 000000000000..76cd7e045473
--- /dev/null
+++ b/libexec/nuageinit/tests/utils.sh
@@ -0,0 +1,32 @@
+#-
+# Copyright (c) 2022 Baptiste Daroussin <bapt@FreeBSD.org>
+# Copyright (c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+atf_test_case warn
+atf_test_case err
+atf_test_case dirname
+
+warn_body()
+{
+ atf_check -e "inline:nuageinit: plop\n" -s exit:0 /usr/libexec/flua $(atf_get_srcdir)/warn.lua
+}
+
+err_body()
+{
+ atf_check -e "inline:nuageinit: plop\n" -s exit:1 /usr/libexec/flua $(atf_get_srcdir)/err.lua
+}
+
+dirname_body()
+{
+ atf_check -o "inline:/my/path/\n" -s exit:0 /usr/libexec/flua $(atf_get_srcdir)/dirname.lua
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case warn
+ atf_add_test_case err
+ atf_add_test_case dirname
+}
diff --git a/libexec/nuageinit/tests/warn.lua b/libexec/nuageinit/tests/warn.lua
new file mode 100644
index 000000000000..ce2b63a8dbf0
--- /dev/null
+++ b/libexec/nuageinit/tests/warn.lua
@@ -0,0 +1,5 @@
+#!/usr/libexec/flua
+
+local n = require("nuage")
+
+n.warn("plop")
diff --git a/libexec/phttpget/Makefile b/libexec/phttpget/Makefile
new file mode 100644
index 000000000000..ef231fa634db
--- /dev/null
+++ b/libexec/phttpget/Makefile
@@ -0,0 +1,4 @@
+PROG= phttpget
+MAN= phttpget.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/phttpget/Makefile.depend b/libexec/phttpget/Makefile.depend
new file mode 100644
index 000000000000..84b8ddd67e34
--- /dev/null
+++ b/libexec/phttpget/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/phttpget/phttpget.8 b/libexec/phttpget/phttpget.8
new file mode 100644
index 000000000000..16e0be65cd4c
--- /dev/null
+++ b/libexec/phttpget/phttpget.8
@@ -0,0 +1,82 @@
+.\"-
+.\" Copyright (c) 2015 Xin LI <delphij@FreeBSD.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 3, 2015
+.Dt PHTTPGET 8
+.Os
+.Sh NAME
+.Nm phttpget
+.Nd retrieve multiple files via pipelined HTTP
+.Sh SYNOPSIS
+.Nm
+.Ar server
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is a minimalist pipelined HTTP client,
+which is used to retrieve multiple
+.Ar file Ns s
+from one
+.Ar server ,
+and saves the downloaded files in the current working directory,
+using the last portion of their download path as file names.
+.Pp
+By making several "in flight" HTTP requests,
+it can dramatically increase performance when a large number of
+small files need to be downloaded.
+.Pp
+The
+.Xr freebsd-update 8
+tool uses
+.Nm
+to download binary patch files.
+.Sh ENVIRONMENT
+.Bl -tag -width HTTP_PROXY_AUTH
+.It Ev HTTP_PROXY
+URL of the proxy to use for HTTP requests.
+.It Ev HTTP_PROXY_AUTH
+Authorization parameters for the HTTP proxy.
+.It Ev HTTP_USER_AGENT
+The User-Agent string to use for HTTP requests.
+The default is
+.Dq phttpget/0.1 .
+.It Ev HTTP_TIMEOUT
+Timeout for HTTP request in seconds.
+.El
+.Sh SEE ALSO
+.Xr fetch 1 ,
+.Xr freebsd-update 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Colin Percival Aq Mt cperciva@FreeBSD.org
+initially for use with
+.Xr portsnap 8
+(now removed) and has been used by
+.Xr freebsd-update 8 .
+This manual page was written by
+.An Xin LI Aq Mt delphij@FreeBSD.org .
diff --git a/libexec/phttpget/phttpget.c b/libexec/phttpget/phttpget.c
new file mode 100644
index 000000000000..33ef1eb04edf
--- /dev/null
+++ b/libexec/phttpget/phttpget.c
@@ -0,0 +1,729 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2005 Colin Percival
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static const char * env_HTTP_PROXY;
+static char * env_HTTP_PROXY_AUTH;
+static const char * env_HTTP_USER_AGENT;
+static char * env_HTTP_TIMEOUT;
+static const char * proxyport;
+static char * proxyauth;
+
+static struct timeval timo = { 15, 0};
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: phttpget server [file ...]\n");
+ exit(EX_USAGE);
+}
+
+/*
+ * Base64 encode a string; the string returned, if non-NULL, is
+ * allocated using malloc() and must be freed by the caller.
+ */
+static char *
+b64enc(const char *ptext)
+{
+ static const char base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+ const char *pt;
+ char *ctext, *pc;
+ size_t ptlen, ctlen;
+ uint32_t t;
+ unsigned int j;
+
+ /*
+ * Encoded length is 4 characters per 3-byte block or partial
+ * block of plaintext, plus one byte for the terminating NUL
+ */
+ ptlen = strlen(ptext);
+ if (ptlen > ((SIZE_MAX - 1) / 4) * 3 - 2)
+ return NULL; /* Possible integer overflow */
+ ctlen = 4 * ((ptlen + 2) / 3) + 1;
+ if ((ctext = malloc(ctlen)) == NULL)
+ return NULL;
+ ctext[ctlen - 1] = 0;
+
+ /*
+ * Scan through ptext, reading up to 3 bytes from ptext and
+ * writing 4 bytes to ctext, until we run out of input.
+ */
+ for (pt = ptext, pc = ctext; ptlen; ptlen -= 3, pc += 4) {
+ /* Read 3 bytes */
+ for (t = j = 0; j < 3; j++) {
+ t <<= 8;
+ if (j < ptlen)
+ t += *pt++;
+ }
+
+ /* Write 4 bytes */
+ for (j = 0; j < 4; j++) {
+ if (j <= ptlen + 1)
+ pc[j] = base64[(t >> 18) & 0x3f];
+ else
+ pc[j] = '=';
+ t <<= 6;
+ }
+
+ /* If we're done, exit the loop */
+ if (ptlen <= 3)
+ break;
+ }
+
+ return (ctext);
+}
+
+static void
+readenv(void)
+{
+ char *proxy_auth_userpass, *proxy_auth_userpass64, *p;
+ char *proxy_auth_user = NULL;
+ char *proxy_auth_pass = NULL;
+ long http_timeout;
+
+ env_HTTP_PROXY = getenv("HTTP_PROXY");
+ if (env_HTTP_PROXY == NULL)
+ env_HTTP_PROXY = getenv("http_proxy");
+ if (env_HTTP_PROXY != NULL) {
+ if (strncmp(env_HTTP_PROXY, "http://", 7) == 0)
+ env_HTTP_PROXY += 7;
+ p = strchr(env_HTTP_PROXY, '/');
+ if (p != NULL)
+ *p = 0;
+ p = strchr(env_HTTP_PROXY, ':');
+ if (p != NULL) {
+ *p = 0;
+ proxyport = p + 1;
+ } else
+ proxyport = "3128";
+ }
+
+ env_HTTP_PROXY_AUTH = getenv("HTTP_PROXY_AUTH");
+ if ((env_HTTP_PROXY != NULL) &&
+ (env_HTTP_PROXY_AUTH != NULL) &&
+ (strncasecmp(env_HTTP_PROXY_AUTH, "basic:" , 6) == 0)) {
+ /* Ignore authentication scheme */
+ (void) strsep(&env_HTTP_PROXY_AUTH, ":");
+
+ /* Ignore realm */
+ (void) strsep(&env_HTTP_PROXY_AUTH, ":");
+
+ /* Obtain username and password */
+ proxy_auth_user = strsep(&env_HTTP_PROXY_AUTH, ":");
+ proxy_auth_pass = env_HTTP_PROXY_AUTH;
+ }
+
+ if ((proxy_auth_user != NULL) && (proxy_auth_pass != NULL)) {
+ asprintf(&proxy_auth_userpass, "%s:%s",
+ proxy_auth_user, proxy_auth_pass);
+ if (proxy_auth_userpass == NULL)
+ err(1, "asprintf");
+
+ proxy_auth_userpass64 = b64enc(proxy_auth_userpass);
+ if (proxy_auth_userpass64 == NULL)
+ err(1, "malloc");
+
+ asprintf(&proxyauth, "Proxy-Authorization: Basic %s\r\n",
+ proxy_auth_userpass64);
+ if (proxyauth == NULL)
+ err(1, "asprintf");
+
+ free(proxy_auth_userpass);
+ free(proxy_auth_userpass64);
+ } else
+ proxyauth = NULL;
+
+ env_HTTP_USER_AGENT = getenv("HTTP_USER_AGENT");
+ if (env_HTTP_USER_AGENT == NULL)
+ env_HTTP_USER_AGENT = "phttpget/0.1";
+
+ env_HTTP_TIMEOUT = getenv("HTTP_TIMEOUT");
+ if (env_HTTP_TIMEOUT != NULL) {
+ http_timeout = strtol(env_HTTP_TIMEOUT, &p, 10);
+ if ((*env_HTTP_TIMEOUT == '\0') || (*p != '\0') ||
+ (http_timeout < 0))
+ warnx("HTTP_TIMEOUT (%s) is not a positive integer",
+ env_HTTP_TIMEOUT);
+ else
+ timo.tv_sec = http_timeout;
+ }
+}
+
+static int
+makerequest(char ** buf, char * path, char * server, int connclose)
+{
+ int buflen;
+
+ buflen = asprintf(buf,
+ "GET %s%s/%s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "User-Agent: %s\r\n"
+ "%s"
+ "%s"
+ "\r\n",
+ env_HTTP_PROXY ? "http://" : "",
+ env_HTTP_PROXY ? server : "",
+ path, server, env_HTTP_USER_AGENT,
+ proxyauth ? proxyauth : "",
+ connclose ? "Connection: Close\r\n" : "Connection: Keep-Alive\r\n");
+ if (buflen == -1)
+ err(1, "asprintf");
+ return(buflen);
+}
+
+static int
+readln(int sd, char * resbuf, int * resbuflen, int * resbufpos)
+{
+ ssize_t len;
+
+ while (strnstr(resbuf + *resbufpos, "\r\n",
+ *resbuflen - *resbufpos) == NULL) {
+ /* Move buffered data to the start of the buffer */
+ if (*resbufpos != 0) {
+ memmove(resbuf, resbuf + *resbufpos,
+ *resbuflen - *resbufpos);
+ *resbuflen -= *resbufpos;
+ *resbufpos = 0;
+ }
+
+ /* If the buffer is full, complain */
+ if (*resbuflen == BUFSIZ)
+ return -1;
+
+ /* Read more data into the buffer */
+ len = recv(sd, resbuf + *resbuflen, BUFSIZ - *resbuflen, 0);
+ if ((len == 0) ||
+ ((len == -1) && (errno != EINTR)))
+ return -1;
+
+ if (len != -1)
+ *resbuflen += len;
+ }
+
+ return 0;
+}
+
+static int
+copybytes(int sd, int fd, off_t copylen, char * resbuf, int * resbuflen,
+ int * resbufpos)
+{
+ ssize_t len;
+
+ while (copylen) {
+ /* Write data from resbuf to fd */
+ len = *resbuflen - *resbufpos;
+ if (copylen < len)
+ len = copylen;
+ if (len > 0) {
+ if (fd != -1)
+ len = write(fd, resbuf + *resbufpos, len);
+ if (len == -1)
+ err(1, "write");
+ *resbufpos += len;
+ copylen -= len;
+ continue;
+ }
+
+ /* Read more data into buffer */
+ len = recv(sd, resbuf, BUFSIZ, 0);
+ if (len == -1) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ } else if (len == 0) {
+ return -2;
+ } else {
+ *resbuflen = len;
+ *resbufpos = 0;
+ }
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct addrinfo hints; /* Hints to getaddrinfo */
+ struct addrinfo *res; /* Pointer to server address being used */
+ struct addrinfo *res0; /* Pointer to server addresses */
+ char * resbuf = NULL; /* Response buffer */
+ int resbufpos = 0; /* Response buffer position */
+ int resbuflen = 0; /* Response buffer length */
+ char * eolp; /* Pointer to "\r\n" within resbuf */
+ char * hln; /* Pointer within header line */
+ char * servername; /* Name of server */
+ char * fname = NULL; /* Name of downloaded file */
+ char * reqbuf = NULL; /* Request buffer */
+ int reqbufpos = 0; /* Request buffer position */
+ int reqbuflen = 0; /* Request buffer length */
+ ssize_t len; /* Length sent or received */
+ int nreq = 0; /* Number of next request to send */
+ int nres = 0; /* Number of next reply to receive */
+ int pipelined = 0; /* != 0 if connection in pipelined mode. */
+ int keepalive; /* != 0 if HTTP/1.0 keep-alive rcvd. */
+ int sd = -1; /* Socket descriptor */
+ int sdflags = 0; /* Flags on the socket sd */
+ int fd = -1; /* Descriptor for download target file */
+ int error; /* Error code */
+ int statuscode; /* HTTP Status code */
+ off_t contentlength; /* Value from Content-Length header */
+ int chunked; /* != if transfer-encoding is chunked */
+ off_t clen; /* Chunk length */
+ int firstreq = 0; /* # of first request for this connection */
+ int val; /* Value used for setsockopt call */
+
+ /* Check that the arguments are sensible */
+ if (argc < 2)
+ usage();
+
+ /* Read important environment variables */
+ readenv();
+
+ /* Get server name and adjust arg[cv] to point at file names */
+ servername = argv[1];
+ argv += 2;
+ argc -= 2;
+
+ /* Allocate response buffer */
+ resbuf = malloc(BUFSIZ);
+ if (resbuf == NULL)
+ err(1, "malloc");
+
+ /* Look up server */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(env_HTTP_PROXY ? env_HTTP_PROXY : servername,
+ env_HTTP_PROXY ? proxyport : "http", &hints, &res0);
+ if (error)
+ errx(1, "host = %s, port = %s: %s",
+ env_HTTP_PROXY ? env_HTTP_PROXY : servername,
+ env_HTTP_PROXY ? proxyport : "http",
+ gai_strerror(error));
+ if (res0 == NULL)
+ errx(1, "could not look up %s", servername);
+ res = res0;
+
+ /* Do the fetching */
+ while (nres < argc) {
+ /* Make sure we have a connected socket */
+ for (; sd == -1; res = res->ai_next) {
+ /* No addresses left to try :-( */
+ if (res == NULL)
+ errx(1, "Could not connect to %s", servername);
+
+ /* Create a socket... */
+ sd = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (sd == -1)
+ continue;
+
+ /* ... set 15-second timeouts ... */
+ setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO,
+ (void *)&timo, (socklen_t)sizeof(timo));
+ setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO,
+ (void *)&timo, (socklen_t)sizeof(timo));
+
+ /* ... disable SIGPIPE generation ... */
+ val = 1;
+ setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE,
+ (void *)&val, sizeof(int));
+
+ /* ... and connect to the server. */
+ if(connect(sd, res->ai_addr, res->ai_addrlen)) {
+ close(sd);
+ sd = -1;
+ continue;
+ }
+
+ firstreq = nres;
+ }
+
+ /*
+ * If in pipelined HTTP mode, put socket into non-blocking
+ * mode, since we're probably going to want to try to send
+ * several HTTP requests.
+ */
+ if (pipelined) {
+ sdflags = fcntl(sd, F_GETFL);
+ if (fcntl(sd, F_SETFL, sdflags | O_NONBLOCK) == -1)
+ err(1, "fcntl");
+ }
+
+ /* Construct requests and/or send them without blocking */
+ while ((nreq < argc) && ((reqbuf == NULL) || pipelined)) {
+ /* If not in the middle of a request, make one */
+ if (reqbuf == NULL) {
+ reqbuflen = makerequest(&reqbuf, argv[nreq],
+ servername, (nreq == argc - 1));
+ reqbufpos = 0;
+ }
+
+ /* If in pipelined mode, try to send the request */
+ if (pipelined) {
+ while (reqbufpos < reqbuflen) {
+ len = send(sd, reqbuf + reqbufpos,
+ reqbuflen - reqbufpos, 0);
+ if (len == -1)
+ break;
+ reqbufpos += len;
+ }
+ if (reqbufpos < reqbuflen) {
+ if (errno != EAGAIN)
+ goto conndied;
+ break;
+ } else {
+ free(reqbuf);
+ reqbuf = NULL;
+ nreq++;
+ }
+ }
+ }
+
+ /* Put connection back into blocking mode */
+ if (pipelined) {
+ if (fcntl(sd, F_SETFL, sdflags) == -1)
+ err(1, "fcntl");
+ }
+
+ /* Do we need to blocking-send a request? */
+ if (nres == nreq) {
+ while (reqbufpos < reqbuflen) {
+ len = send(sd, reqbuf + reqbufpos,
+ reqbuflen - reqbufpos, 0);
+ if (len == -1)
+ goto conndied;
+ reqbufpos += len;
+ }
+ free(reqbuf);
+ reqbuf = NULL;
+ nreq++;
+ }
+
+ /* Scan through the response processing headers. */
+ statuscode = 0;
+ contentlength = -1;
+ chunked = 0;
+ keepalive = 0;
+ do {
+ /* Get a header line */
+ error = readln(sd, resbuf, &resbuflen, &resbufpos);
+ if (error)
+ goto conndied;
+ hln = resbuf + resbufpos;
+ eolp = strnstr(hln, "\r\n", resbuflen - resbufpos);
+ resbufpos = (eolp - resbuf) + 2;
+ *eolp = '\0';
+
+ /* Make sure it doesn't contain a NUL character */
+ if (strchr(hln, '\0') != eolp)
+ goto conndied;
+
+ if (statuscode == 0) {
+ /* The first line MUST be HTTP/1.x xxx ... */
+ if ((strncmp(hln, "HTTP/1.", 7) != 0) ||
+ ! isdigit(hln[7]))
+ goto conndied;
+
+ /*
+ * If the minor version number isn't zero,
+ * then we can assume that pipelining our
+ * requests is OK -- as long as we don't
+ * see a "Connection: close" line later
+ * and we either have a Content-Length or
+ * Transfer-Encoding: chunked header to
+ * tell us the length.
+ */
+ if (hln[7] != '0')
+ pipelined = 1;
+
+ /* Skip over the minor version number */
+ hln = strchr(hln + 7, ' ');
+ if (hln == NULL)
+ goto conndied;
+ else
+ hln++;
+
+ /* Read the status code */
+ while (isdigit(*hln)) {
+ statuscode = statuscode * 10 +
+ *hln - '0';
+ hln++;
+ }
+
+ if (statuscode < 100 || statuscode > 599)
+ goto conndied;
+
+ /* Ignore the rest of the line */
+ continue;
+ }
+
+ /*
+ * Check for "Connection: close" or
+ * "Connection: Keep-Alive" header
+ */
+ if (strncasecmp(hln, "Connection:", 11) == 0) {
+ hln += 11;
+ if (strcasestr(hln, "close") != NULL)
+ pipelined = 0;
+ if (strcasestr(hln, "Keep-Alive") != NULL)
+ keepalive = 1;
+
+ /* Next header... */
+ continue;
+ }
+
+ /* Check for "Content-Length:" header */
+ if (strncasecmp(hln, "Content-Length:", 15) == 0) {
+ hln += 15;
+ contentlength = 0;
+
+ /* Find the start of the length */
+ while (!isdigit(*hln) && (*hln != '\0'))
+ hln++;
+
+ /* Compute the length */
+ while (isdigit(*hln)) {
+ if (contentlength >= OFF_MAX / 10) {
+ /* Nasty people... */
+ goto conndied;
+ }
+ contentlength = contentlength * 10 +
+ *hln - '0';
+ hln++;
+ }
+
+ /* Next header... */
+ continue;
+ }
+
+ /* Check for "Transfer-Encoding: chunked" header */
+ if (strncasecmp(hln, "Transfer-Encoding:", 18) == 0) {
+ hln += 18;
+ if (strcasestr(hln, "chunked") != NULL)
+ chunked = 1;
+
+ /* Next header... */
+ continue;
+ }
+
+ /* We blithely ignore any other header lines */
+
+ /* No more header lines */
+ if (strlen(hln) == 0) {
+ /*
+ * If the status code was 1xx, then there will
+ * be a real header later. Servers may emit
+ * 1xx header blocks at will, but since we
+ * don't expect one, we should just ignore it.
+ */
+ if (100 <= statuscode && statuscode <= 199) {
+ statuscode = 0;
+ continue;
+ }
+
+ /* End of header; message body follows */
+ break;
+ }
+ } while (1);
+
+ /* No message body for 204 or 304 */
+ if (statuscode == 204 || statuscode == 304) {
+ nres++;
+ continue;
+ }
+
+ /*
+ * There should be a message body coming, but we only want
+ * to send it to a file if the status code is 200
+ */
+ if (statuscode == 200) {
+ /* Generate a file name for the download */
+ fname = strrchr(argv[nres], '/');
+ if (fname == NULL)
+ fname = argv[nres];
+ else
+ fname++;
+ if (strlen(fname) == 0)
+ errx(1, "Cannot obtain file name from %s\n",
+ argv[nres]);
+
+ fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+ if (fd == -1)
+ errx(1, "open(%s)", fname);
+ }
+
+ /* Read the message and send data to fd if appropriate */
+ if (chunked) {
+ /* Handle a chunked-encoded entity */
+
+ /* Read chunks */
+ do {
+ error = readln(sd, resbuf, &resbuflen,
+ &resbufpos);
+ if (error)
+ goto conndied;
+ hln = resbuf + resbufpos;
+ eolp = strstr(hln, "\r\n");
+ resbufpos = (eolp - resbuf) + 2;
+
+ clen = 0;
+ while (isxdigit(*hln)) {
+ if (clen >= OFF_MAX / 16) {
+ /* Nasty people... */
+ goto conndied;
+ }
+ if (isdigit(*hln))
+ clen = clen * 16 + *hln - '0';
+ else
+ clen = clen * 16 + 10 +
+ tolower(*hln) - 'a';
+ hln++;
+ }
+
+ error = copybytes(sd, fd, clen, resbuf,
+ &resbuflen, &resbufpos);
+ if (error) {
+ goto conndied;
+ }
+ } while (clen != 0);
+
+ /* Read trailer and final CRLF */
+ do {
+ error = readln(sd, resbuf, &resbuflen,
+ &resbufpos);
+ if (error)
+ goto conndied;
+ hln = resbuf + resbufpos;
+ eolp = strstr(hln, "\r\n");
+ resbufpos = (eolp - resbuf) + 2;
+ } while (hln != eolp);
+ } else if (contentlength != -1) {
+ error = copybytes(sd, fd, contentlength, resbuf,
+ &resbuflen, &resbufpos);
+ if (error)
+ goto conndied;
+ } else {
+ /*
+ * Not chunked, and no content length header.
+ * Read everything until the server closes the
+ * socket.
+ */
+ error = copybytes(sd, fd, OFF_MAX, resbuf,
+ &resbuflen, &resbufpos);
+ if (error == -1)
+ goto conndied;
+ pipelined = 0;
+ }
+
+ if (fd != -1) {
+ close(fd);
+ fd = -1;
+ }
+
+ fprintf(stderr, "http://%s/%s: %d ", servername, argv[nres],
+ statuscode);
+ if (statuscode == 200)
+ fprintf(stderr, "OK\n");
+ else if (statuscode < 300)
+ fprintf(stderr, "Successful (ignored)\n");
+ else if (statuscode < 400)
+ fprintf(stderr, "Redirection (ignored)\n");
+ else
+ fprintf(stderr, "Error (ignored)\n");
+
+ /* We've finished this file! */
+ nres++;
+
+ /*
+ * If necessary, clean up this connection so that we
+ * can start a new one.
+ */
+ if (pipelined == 0 && keepalive == 0)
+ goto cleanupconn;
+ continue;
+
+conndied:
+ /*
+ * Something went wrong -- our connection died, the server
+ * sent us garbage, etc. If this happened on the first
+ * request we sent over this connection, give up. Otherwise,
+ * close this connection, open a new one, and reissue the
+ * request.
+ */
+ if (nres == firstreq)
+ errx(1, "Connection failure");
+
+cleanupconn:
+ /*
+ * Clean up our connection and keep on going
+ */
+ shutdown(sd, SHUT_RDWR);
+ close(sd);
+ sd = -1;
+ if (fd != -1) {
+ close(fd);
+ fd = -1;
+ }
+ if (reqbuf != NULL) {
+ free(reqbuf);
+ reqbuf = NULL;
+ }
+ nreq = nres;
+ res = res0;
+ pipelined = 0;
+ resbufpos = resbuflen = 0;
+ continue;
+ }
+
+ free(resbuf);
+ freeaddrinfo(res0);
+
+ return 0;
+}
diff --git a/libexec/pppoed/Makefile b/libexec/pppoed/Makefile
new file mode 100644
index 000000000000..26339249c3b5
--- /dev/null
+++ b/libexec/pppoed/Makefile
@@ -0,0 +1,9 @@
+PACKAGE=ppp
+PROG= pppoed
+LIBADD= netgraph
+MAN= pppoed.8
+
+WARNS?= 1
+WFORMAT=0
+
+.include <bsd.prog.mk>
diff --git a/libexec/pppoed/Makefile.depend b/libexec/pppoed/Makefile.depend
new file mode 100644
index 000000000000..29f4fca87a58
--- /dev/null
+++ b/libexec/pppoed/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetgraph \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/pppoed/pppoed.8 b/libexec/pppoed/pppoed.8
new file mode 100644
index 000000000000..35d62a9b58fd
--- /dev/null
+++ b/libexec/pppoed/pppoed.8
@@ -0,0 +1,217 @@
+.\"-
+.\" Copyright (c) 1999-2001 Brian Somers <brian@Awfulhak.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd November 8, 1999
+.Dt PPPOED 8
+.Os
+.Sh NAME
+.Nm pppoed
+.Nd handle incoming PPP over Ethernet connections
+.Sh SYNOPSIS
+.Nm
+.Op Fl Fd\&
+.Op Fl P Ar pidfile
+.Op Fl a Ar name
+.Op Fl e Ar exec | Fl l Ar label
+.Op Fl n Ar ngdebug
+.Op Fl p Ar provider
+.Ar interface
+.Sh DESCRIPTION
+The
+.Nm
+utility listens to the given
+.Ar interface
+for PPP over Ethernet (PPPoE) service request packets, and actions them
+by negotiating a session then invoking a
+.Xr ppp 8
+program.
+The negotiation is implemented by the
+.Dq pppoe
+netgraph node.
+See
+.Xr ng_pppoe 4
+for details.
+.Pp
+The
+.Nm
+utility
+will only offer services to clients requesting services from the given
+.Ar provider ,
+which is taken as an empty name if not provided.
+If a provider name of
+.Dq *
+is given, any PPPoE requests will be offered service.
+.Pp
+The supplied
+.Ar name
+will be given as the access concentrator name when establishing the connection.
+If no
+.Ar name
+is given, the current base hostname is used.
+.Pp
+After receiving a request (PADI) from the PPPoE netgraph node,
+.Nm
+.Xr fork 2 Ns s
+a child process and returns to service further requests.
+The child process offers service
+(using
+.Ar name )
+and waits for a
+.Dv SUCCESS
+indication from the PPPoE node.
+On receipt of the
+.Dv SUCCESS
+indication,
+.Nm
+will execute
+.Pp
+.D1 Ic exec Pa /usr/sbin/ppp Fl direct Ar label
+.Pp
+as a shell sub-process.
+If
+.Ar label
+has not been specified, it defaults to
+.Ar provider .
+It is possible to specify another command using the
+.Ar exec
+argument.
+This is mandatory if
+.Ar provider
+and
+.Ar label
+are not given.
+The child process will have standard input and standard output
+attached to the same
+.Xr netgraph 4
+data socket
+(see
+.Xr ng_socket 4 )
+when started.
+.Pp
+The environment variables
+.Ev HISMACADDR
+and
+.Ev ACNAME
+are made available to the child process and are set to the MAC address of
+the peer and the name of the AC respectively.
+.Pp
+Upon invocation,
+.Nm
+will attach a
+.Dq pppoe
+netgraph node to the relevant
+.Dq ether
+node using
+.Dq Ar interface Ns \&:
+as the node name, and then connect that
+.Dq pppoe
+node to a local
+.Dq socket
+node.
+If the
+.Fl F
+option has not been given,
+.Nm
+will then go into the background and disassociate itself from the controlling
+terminal.
+When the
+.Fl F
+option is given,
+.Nm
+stays in the foreground.
+.Pp
+If the
+.Fl d
+option is given, additional diagnostics are provided (see the
+.Sx DIAGNOSTICS
+section below).
+If the
+.Fl n
+option is given,
+.Fn NgSetDebug
+is called with an argument of
+.Ar ngdebug .
+.Pp
+If
+.Ar pidfile
+is given,
+.Nm
+will write its process ID to this file on startup.
+.Sh DIAGNOSTICS
+After creating the necessary
+.Xr netgraph 4
+nodes as described above,
+.Nm
+uses
+.Xr syslogd 8
+to report all incoming connections.
+If the
+.Fl d
+option is given,
+.Nm
+will report on the child processes creation of a new netgraph socket, its
+service offer and the invocation of the
+.Xr ppp 8
+program.
+If the
+.Fl n
+option is given, netgraph diagnostic messages are also redirected to
+.Xr syslogd 8 .
+.Pp
+It is sometimes useful to add the following to
+.Pa /etc/syslog.conf :
+.Bd -literal -offset indent
+!pppoed
+*.* /var/log/pppoed.log
+.Ed
+.Pp
+and the following to
+.Pa /etc/newsyslog.conf :
+.Pp
+.Dl "/var/log/pppoed.log 640 3 100 * Z"
+.Sh SEE ALSO
+.Xr NgSetDebug 3 ,
+.Xr netgraph 4 ,
+.Xr ng_ether 4 ,
+.Xr ng_pppoe 4 ,
+.Xr ng_socket 4 ,
+.Xr syslog.conf 5 ,
+.Xr ppp 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+utility was written by
+.An Brian Somers Aq Mt brian@Awfulhak.org
+and first appeared in
+.Fx 3.4 .
+.Sh BUGS
+If another netgraph node is using the given interface,
+.Nm
+will fail to start.
+This is because
+.Xr netgraph 4
+does not currently allow node chaining.
+This may change in the future.
diff --git a/libexec/pppoed/pppoed.c b/libexec/pppoed/pppoed.c
new file mode 100644
index 000000000000..0dbdb20cbc82
--- /dev/null
+++ b/libexec/pppoed/pppoed.c
@@ -0,0 +1,692 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1999-2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netgraph.h>
+#include <net/ethernet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netgraph/ng_ether.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_pppoe.h>
+#include <netgraph/ng_socket.h>
+
+#include <errno.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/fcntl.h>
+#ifndef NOKLDLOAD
+#include <sys/linker.h>
+#include <sys/module.h>
+#endif
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+
+
+#define DEFAULT_EXEC_PREFIX "exec /usr/sbin/ppp -direct "
+#define HISMACADDR "HISMACADDR"
+#define SESSION_ID "SESSION_ID"
+
+static void nglogx(const char *, ...) __printflike(1, 2);
+
+static int ReceivedSignal;
+
+static int
+usage(const char *prog)
+{
+ fprintf(stderr, "usage: %s [-Fd] [-P pidfile] [-a name] [-e exec | -l label]"
+ " [-n ngdebug] [-p provider] interface\n", prog);
+ return EX_USAGE;
+}
+
+static void
+Farewell(int sig)
+{
+ ReceivedSignal = sig;
+}
+
+static int
+ConfigureNode(const char *prog, const char *iface, const char *provider,
+ int cs, int ds, int debug, struct ngm_connect *ngc)
+{
+ /*
+ * We're going to do this with the passed `ds' & `cs' descriptors:
+ *
+ * .---------.
+ * | ether |
+ * | <iface> |
+ * `---------'
+ * (orphan) ds cs
+ * | | |
+ * | | |
+ * (ethernet) | |
+ * .---------. .-----------.
+ * | pppoe | | socket |
+ * | <iface> |(pppoe-<pid>)<---->(pppoe-<pid>)| <unnamed> |
+ * `--------- `-----------'
+ * (exec-<pid>)
+ * ^ .-----------. .-------------.
+ * | | socket | | ppp -direct |
+ * `--->(exec-<pid>)| <unnamed> |--fd--| provider |
+ * `-----------' `-------------'
+ *
+ * where there are potentially many ppp processes running off of the
+ * same PPPoE node.
+ * The exec-<pid> hook isn't made 'till we Spawn().
+ */
+
+ char *epath, *spath;
+ struct ngpppoe_init_data *data;
+ const struct hooklist *hlist;
+ const struct nodeinfo *ninfo;
+ const struct linkinfo *nlink;
+ struct ngm_mkpeer mkp;
+ struct ng_mesg *resp;
+ u_char rbuf[2048];
+ int f, plen;
+
+ /*
+ * Ask for a list of hooks attached to the "ether" node. This node should
+ * magically exist as a way of hooking stuff onto an ethernet device
+ */
+ epath = (char *)alloca(strlen(iface) + 2);
+ sprintf(epath, "%s:", iface);
+
+ if (debug)
+ fprintf(stderr, "Sending NGM_LISTHOOKS to %s\n", epath);
+
+ if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) {
+ if (errno == ENOENT)
+ fprintf(stderr, "%s Cannot send a netgraph message: Invalid interface\n",
+ epath);
+ else
+ fprintf(stderr, "%s Cannot send a netgraph message: %s\n",
+ epath, strerror(errno));
+ return EX_UNAVAILABLE;
+ }
+
+ /* Get our list back */
+ resp = (struct ng_mesg *)rbuf;
+ if (NgRecvMsg(cs, resp, sizeof rbuf, NULL) <= 0) {
+ perror("Cannot get netgraph response");
+ return EX_UNAVAILABLE;
+ }
+
+ hlist = (const struct hooklist *)resp->data;
+ ninfo = &hlist->nodeinfo;
+
+ if (debug)
+ fprintf(stderr, "Got reply from id [%x]: Type %s with %d hooks\n",
+ ninfo->id, ninfo->type, ninfo->hooks);
+
+ /* Make sure we've got the right type of node */
+ if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) {
+ fprintf(stderr, "%s Unexpected node type ``%s'' (wanted ``"
+ NG_ETHER_NODE_TYPE "'')\n", epath, ninfo->type);
+ return EX_DATAERR;
+ }
+
+ /* look for a hook already attached. */
+ for (f = 0; f < ninfo->hooks; f++) {
+ nlink = &hlist->link[f];
+
+ if (debug)
+ fprintf(stderr, " Got [%x]:%s -> [%x]:%s\n", ninfo->id,
+ nlink->ourhook, nlink->nodeinfo.id, nlink->peerhook);
+
+ if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
+ !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
+ /*
+ * Something is using the data coming out of this `ether' node.
+ * If it's a PPPoE node, we use that node, otherwise we complain that
+ * someone else is using the node.
+ */
+ if (strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) {
+ fprintf(stderr, "%s Node type %s is currently active\n",
+ epath, nlink->nodeinfo.type);
+ return EX_UNAVAILABLE;
+ }
+ break;
+ }
+ }
+
+ if (f == ninfo->hooks) {
+ /*
+ * Create a new PPPoE node connected to the `ether' node using
+ * the magic `orphan' and `ethernet' hooks
+ */
+ snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
+ snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
+ snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
+
+ if (debug)
+ fprintf(stderr, "Send MKPEER: %s%s -> [type %s]:%s\n", epath,
+ mkp.ourhook, mkp.type, mkp.peerhook);
+
+ if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &mkp, sizeof mkp) < 0) {
+ fprintf(stderr, "%s Cannot create a peer PPPoE node: %s\n",
+ epath, strerror(errno));
+ return EX_OSERR;
+ }
+ }
+
+ /* Connect the PPPoE node to our socket node. */
+ snprintf(ngc->path, sizeof ngc->path, "%s%s", epath, NG_ETHER_HOOK_ORPHAN);
+ snprintf(ngc->ourhook, sizeof ngc->ourhook, "pppoe-%ld", (long)getpid());
+ memcpy(ngc->peerhook, ngc->ourhook, sizeof ngc->peerhook);
+
+ if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
+ NGM_CONNECT, ngc, sizeof *ngc) < 0) {
+ perror("Cannot CONNECT PPPoE and socket nodes");
+ return EX_OSERR;
+ }
+
+ plen = strlen(provider);
+
+ data = (struct ngpppoe_init_data *)alloca(sizeof *data + plen);
+ snprintf(data->hook, sizeof data->hook, "%s", ngc->peerhook);
+ memcpy(data->data, provider, plen);
+ data->data_len = plen;
+
+ spath = (char *)alloca(strlen(ngc->peerhook) + 3);
+ strcpy(spath, ".:");
+ strcpy(spath + 2, ngc->ourhook);
+
+ if (debug) {
+ if (provider)
+ fprintf(stderr, "Sending PPPOE_LISTEN to %s, provider %s\n",
+ spath, provider);
+ else
+ fprintf(stderr, "Sending PPPOE_LISTEN to %s\n", spath);
+ }
+
+ if (NgSendMsg(cs, spath, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN,
+ data, sizeof *data + plen) == -1) {
+ fprintf(stderr, "%s: Cannot LISTEN on netgraph node: %s\n",
+ spath, strerror(errno));
+ return EX_OSERR;
+ }
+
+ return 0;
+}
+
+static void
+Spawn(const char *prog, const char *acname, const char *provider,
+ const char *exec, struct ngm_connect ngc, int cs, int ds, void *request,
+ int sz, int debug)
+{
+ char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
+ struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
+ struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
+ struct ngpppoe_init_data *data;
+ char env[18], unknown[14], sessionid[5], *path;
+ unsigned char *macaddr;
+ const char *msg;
+ int ret, slen;
+
+ switch ((ret = fork())) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ break;
+
+ case 0:
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ _exit(errno);
+ default:
+ _exit(0);
+ }
+ close(cs);
+ close(ds);
+
+ /* Create a new socket node */
+ if (debug)
+ syslog(LOG_INFO, "Creating a new socket node");
+
+ if (NgMkSockNode(NULL, &cs, &ds) == -1) {
+ syslog(LOG_ERR, "Cannot create netgraph socket node: %m");
+ _exit(EX_CANTCREAT);
+ }
+
+ /* Connect the PPPoE node to our new socket node. */
+ snprintf(ngc.ourhook, sizeof ngc.ourhook, "exec-%ld", (long)getpid());
+ memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
+
+ if (debug)
+ syslog(LOG_INFO, "Sending CONNECT from .:%s -> %s.%s",
+ ngc.ourhook, ngc.path, ngc.peerhook);
+ if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
+ NGM_CONNECT, &ngc, sizeof ngc) < 0) {
+ syslog(LOG_ERR, "Cannot CONNECT PPPoE and socket nodes: %m");
+ _exit(EX_OSERR);
+ }
+
+ /*
+ * If we tell the socket node not to LINGER, it will go away when
+ * the last hook is removed.
+ */
+ if (debug)
+ syslog(LOG_INFO, "Sending NGM_SOCK_CMD_NOLINGER to socket");
+ if (NgSendMsg(cs, ".:", NGM_SOCKET_COOKIE,
+ NGM_SOCK_CMD_NOLINGER, NULL, 0) < 0) {
+ syslog(LOG_ERR, "Cannot send NGM_SOCK_CMD_NOLINGER: %m");
+ _exit(EX_OSERR);
+ }
+
+ /* Put the PPPoE node into OFFER mode */
+ slen = strlen(acname);
+ data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
+ snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
+ memcpy(data->data, acname, slen);
+ data->data_len = slen;
+
+ path = (char *)alloca(strlen(ngc.ourhook) + 3);
+ strcpy(path, ".:");
+ strcpy(path + 2, ngc.ourhook);
+
+ syslog(LOG_INFO, "Offering to %s as access concentrator %s",
+ path, acname);
+ if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER,
+ data, sizeof *data + slen) == -1) {
+ syslog(LOG_INFO, "%s: Cannot OFFER on netgraph node: %m", path);
+ _exit(EX_OSERR);
+ }
+ /* If we have a provider code, set it */
+ if (provider) {
+ slen = strlen(provider);
+ data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
+ snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
+ memcpy(data->data, provider, slen);
+ data->data_len = slen;
+
+ syslog(LOG_INFO, "adding to %s as offered service %s",
+ path, acname);
+ if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SERVICE,
+ data, sizeof *data + slen) == -1) {
+ syslog(LOG_INFO, "%s: Cannot add service on netgraph node: %m", path);
+ _exit(EX_OSERR);
+ }
+ }
+
+ /* Put the peer's MAC address in the environment */
+ if (sz >= sizeof(struct ether_header)) {
+ macaddr = ((struct ether_header *)request)->ether_shost;
+ snprintf(env, sizeof(env), "%x:%x:%x:%x:%x:%x",
+ macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4],
+ macaddr[5]);
+ if (setenv(HISMACADDR, env, 1) != 0)
+ syslog(LOG_INFO, "setenv: cannot set %s: %m", HISMACADDR);
+ }
+
+ /* And send our request data to the waiting node */
+ if (debug)
+ syslog(LOG_INFO, "Sending original request to %s (%d bytes)", path, sz);
+ if (NgSendData(ds, ngc.ourhook, request, sz) == -1) {
+ syslog(LOG_ERR, "Cannot send original request to %s: %m", path);
+ _exit(EX_OSERR);
+ }
+
+ /* Then wait for a success indication */
+
+ if (debug)
+ syslog(LOG_INFO, "Waiting for a SUCCESS reply %s", path);
+
+ do {
+ if ((ret = NgRecvMsg(cs, rep, sizeof msgbuf, NULL)) < 0) {
+ syslog(LOG_ERR, "%s: Cannot receive a message: %m", path);
+ _exit(EX_OSERR);
+ }
+
+ if (ret == 0) {
+ /* The socket has been closed */
+ syslog(LOG_INFO, "%s: Client timed out", path);
+ _exit(EX_TEMPFAIL);
+ }
+
+ if (rep->header.version != NG_VERSION) {
+ syslog(LOG_ERR, "%ld: Unexpected netgraph version, expected %ld",
+ (long)rep->header.version, (long)NG_VERSION);
+ _exit(EX_PROTOCOL);
+ }
+
+ if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
+ syslog(LOG_INFO, "%ld: Unexpected netgraph cookie, expected %ld",
+ (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
+ continue;
+ }
+
+ switch (rep->header.cmd) {
+ case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break;
+ case NGM_PPPOE_CONNECT: msg = "CONNECT"; break;
+ case NGM_PPPOE_LISTEN: msg = "LISTEN"; break;
+ case NGM_PPPOE_OFFER: msg = "OFFER"; break;
+ case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break;
+ case NGM_PPPOE_FAIL: msg = "FAIL"; break;
+ case NGM_PPPOE_CLOSE: msg = "CLOSE"; break;
+ case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break;
+ case NGM_PPPOE_ACNAME:
+ msg = "ACNAME";
+ if (setenv("ACNAME", sts->hook, 1) != 0)
+ syslog(LOG_WARNING, "setenv: cannot set ACNAME=%s: %m",
+ sts->hook);
+ break;
+ case NGM_PPPOE_SESSIONID:
+ msg = "SESSIONID";
+ snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts);
+ if (setenv("SESSIONID", sessionid, 1) != 0)
+ syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m",
+ sessionid);
+ break;
+ default:
+ snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
+ msg = unknown;
+ break;
+ }
+
+ switch (rep->header.cmd) {
+ case NGM_PPPOE_FAIL:
+ case NGM_PPPOE_CLOSE:
+ syslog(LOG_ERR, "Received NGM_PPPOE_%s (hook \"%s\")",
+ msg, sts->hook);
+ _exit(0);
+ }
+
+ syslog(LOG_INFO, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook);
+ } while (rep->header.cmd != NGM_PPPOE_SUCCESS);
+
+ dup2(ds, STDIN_FILENO);
+ dup2(ds, STDOUT_FILENO);
+ close(ds);
+ close(cs);
+
+ setsid();
+ syslog(LOG_INFO, "Executing: %s", exec);
+ execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", exec, (char *)NULL);
+ syslog(LOG_ERR, "execlp failed: %m");
+ _exit(EX_OSFILE);
+
+ default:
+ wait(&ret);
+ errno = ret;
+ if (errno)
+ syslog(LOG_ERR, "Second fork failed: %m");
+ break;
+ }
+}
+
+#ifndef NOKLDLOAD
+static int
+LoadModules(void)
+{
+ const char *module[] = { "netgraph", "ng_socket", "ng_ether", "ng_pppoe" };
+ int f;
+
+ for (f = 0; f < sizeof module / sizeof *module; f++)
+ if (modfind(module[f]) == -1 && kldload(module[f]) == -1) {
+ fprintf(stderr, "kldload: %s: %s\n", module[f], strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+#endif
+
+static void
+nglog(const char *fmt, ...)
+{
+ char nfmt[256];
+ va_list ap;
+
+ snprintf(nfmt, sizeof nfmt, "%s: %s", fmt, strerror(errno));
+ va_start(ap, fmt);
+ vsyslog(LOG_INFO, nfmt, ap);
+ va_end(ap);
+}
+
+static void
+nglogx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(LOG_INFO, fmt, ap);
+ va_end(ap);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char hostname[MAXHOSTNAMELEN], *exec, rhook[NG_HOOKSIZ];
+ unsigned char response[1024];
+ const char *label, *prog, *provider, *acname;
+ struct ngm_connect ngc;
+ struct sigaction act;
+ int ch, cs, ds, ret, optF, optd, optn, sz, f;
+ const char *pidfile;
+
+ prog = strrchr(argv[0], '/');
+ prog = prog ? prog + 1 : argv[0];
+ pidfile = NULL;
+ exec = NULL;
+ label = NULL;
+ acname = NULL;
+ provider = "";
+ optF = optd = optn = 0;
+
+ while ((ch = getopt(argc, argv, "FP:a:de:l:n:p:")) != -1) {
+ switch (ch) {
+ case 'F':
+ optF = 1;
+ break;
+
+ case 'P':
+ pidfile = optarg;
+ break;
+
+ case 'a':
+ acname = optarg;
+ break;
+
+ case 'd':
+ optd = 1;
+ break;
+
+ case 'e':
+ exec = optarg;
+ break;
+
+ case 'l':
+ label = optarg;
+ break;
+
+ case 'n':
+ optn = 1;
+ NgSetDebug(atoi(optarg));
+ break;
+
+ case 'p':
+ provider = optarg;
+ break;
+
+ default:
+ return usage(prog);
+ }
+ }
+
+ if (optind >= argc || optind + 2 < argc)
+ return usage(prog);
+
+ if (exec != NULL && label != NULL)
+ return usage(prog);
+
+ if (exec == NULL) {
+ if (label == NULL)
+ label = provider;
+ if (label == NULL) {
+ fprintf(stderr, "%s: Either a provider, a label or an exec command"
+ " must be given\n", prog);
+ return usage(prog);
+ }
+ exec = (char *)alloca(sizeof DEFAULT_EXEC_PREFIX + strlen(label));
+ if (exec == NULL) {
+ fprintf(stderr, "%s: Cannot allocate %zu bytes\n", prog,
+ sizeof DEFAULT_EXEC_PREFIX + strlen(label));
+ return EX_OSERR;
+ }
+ strcpy(exec, DEFAULT_EXEC_PREFIX);
+ strcpy(exec + sizeof DEFAULT_EXEC_PREFIX - 1, label);
+ }
+
+ if (acname == NULL) {
+ char *dot;
+
+ if (gethostname(hostname, sizeof hostname))
+ strcpy(hostname, "localhost");
+ else if ((dot = strchr(hostname, '.')))
+ *dot = '\0';
+
+ acname = hostname;
+ }
+
+#ifndef NOKLDLOAD
+ if (!LoadModules())
+ return EX_UNAVAILABLE;
+#endif
+
+ /* Create a socket node */
+ if (NgMkSockNode(NULL, &cs, &ds) == -1) {
+ perror("Cannot create netgraph socket node");
+ return EX_CANTCREAT;
+ }
+
+ /* Connect it up (and fill in `ngc') */
+ if ((ret = ConfigureNode(prog, argv[optind], provider, cs, ds,
+ optd, &ngc)) != 0) {
+ close(cs);
+ close(ds);
+ return ret;
+ }
+
+ if (!optF && daemon(1, 0) == -1) {
+ perror("daemon()");
+ close(cs);
+ close(ds);
+ return EX_OSERR;
+ }
+
+
+ if (pidfile != NULL) {
+ FILE *fp;
+
+ if ((fp = fopen(pidfile, "w")) == NULL) {
+ perror(pidfile);
+ close(cs);
+ close(ds);
+ return EX_CANTCREAT;
+ } else {
+ fprintf(fp, "%d\n", (int)getpid());
+ fclose(fp);
+ }
+ }
+
+ openlog(prog, LOG_PID | (optF ? LOG_PERROR : 0), LOG_DAEMON);
+ if (!optF && optn)
+ NgSetErrLog(nglog, nglogx);
+
+ memset(&act, '\0', sizeof act);
+ act.sa_handler = Farewell;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+
+ while (!ReceivedSignal) {
+ if (*provider)
+ syslog(LOG_INFO, "Listening as provider %s", provider);
+ else
+ syslog(LOG_INFO, "Listening");
+
+ switch (sz = NgRecvData(ds, response, sizeof response, rhook)) {
+ case -1:
+ syslog(LOG_INFO, "NgRecvData: %m");
+ break;
+ case 0:
+ syslog(LOG_INFO, "NgRecvData: socket closed");
+ break;
+ default:
+ if (optd) {
+ char *dbuf, *ptr;
+
+ ptr = dbuf = alloca(sz * 2 + 1);
+ for (f = 0; f < sz; f++, ptr += 2)
+ sprintf(ptr, "%02x", (u_char)response[f]);
+ *ptr = '\0';
+ syslog(LOG_INFO, "Got %d bytes of data: %s", sz, dbuf);
+ }
+ }
+ if (sz <= 0) {
+ ret = EX_UNAVAILABLE;
+ break;
+ }
+ Spawn(prog, acname, provider, exec, ngc, cs, ds, response, sz, optd);
+ }
+
+ if (pidfile)
+ remove(pidfile);
+
+ if (ReceivedSignal) {
+ syslog(LOG_INFO, "Received signal %d, exiting", ReceivedSignal);
+
+ signal(ReceivedSignal, SIG_DFL);
+ raise(ReceivedSignal);
+
+ /* NOTREACHED */
+
+ ret = -ReceivedSignal;
+ }
+
+ return ret;
+}
diff --git a/libexec/rbootd/Makefile b/libexec/rbootd/Makefile
new file mode 100644
index 000000000000..7df1ecee3278
--- /dev/null
+++ b/libexec/rbootd/Makefile
@@ -0,0 +1,8 @@
+PROG= rbootd
+SRCS= bpf.c conf.c parseconf.c rbootd.c rmpproto.c utils.c
+MAN= rbootd.8
+
+WARNS?= 1
+WFORMAT=0
+
+.include <bsd.prog.mk>
diff --git a/libexec/rbootd/Makefile.depend b/libexec/rbootd/Makefile.depend
new file mode 100644
index 000000000000..6ef78fac5cbf
--- /dev/null
+++ b/libexec/rbootd/Makefile.depend
@@ -0,0 +1,15 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rbootd/bpf.c b/libexec/rbootd/bpf.c
new file mode 100644
index 000000000000..449d9bb5567b
--- /dev/null
+++ b/libexec/rbootd/bpf.c
@@ -0,0 +1,398 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Utah Hdr: bpf.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/bpf.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "defs.h"
+#include "pathnames.h"
+
+static int BpfFd = -1;
+static unsigned BpfLen = 0;
+static u_int8_t *BpfPkt = NULL;
+
+/*
+** BpfOpen -- Open and initialize a BPF device.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** File descriptor of opened BPF device (for select() etc).
+**
+** Side Effects:
+** If an error is encountered, the program terminates here.
+*/
+int
+BpfOpen(void)
+{
+ struct ifreq ifr;
+ char bpfdev[32];
+ int n = 0;
+
+ /*
+ * Open the first available BPF device.
+ */
+ do {
+ (void) sprintf(bpfdev, _PATH_BPF, n++);
+ BpfFd = open(bpfdev, O_RDWR);
+ } while (BpfFd < 0 && (errno == EBUSY || errno == EPERM));
+
+ if (BpfFd < 0) {
+ syslog(LOG_ERR, "bpf: no available devices: %m");
+ Exit(0);
+ }
+
+ /*
+ * Set interface name for bpf device, get data link layer
+ * type and make sure it's type Ethernet.
+ */
+ (void) strncpy(ifr.ifr_name, IntfName, sizeof(ifr.ifr_name));
+ if (ioctl(BpfFd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCSETIF,%s): %m", IntfName);
+ Exit(0);
+ }
+
+ /*
+ * Make sure we are dealing with an Ethernet device.
+ */
+ if (ioctl(BpfFd, BIOCGDLT, (caddr_t)&n) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCGDLT): %m");
+ Exit(0);
+ }
+ if (n != DLT_EN10MB) {
+ syslog(LOG_ERR,"bpf: %s: data-link type %d unsupported",
+ IntfName, n);
+ Exit(0);
+ }
+
+ /*
+ * On read(), return packets immediately (do not buffer them).
+ */
+ n = 1;
+ if (ioctl(BpfFd, BIOCIMMEDIATE, (caddr_t)&n) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCIMMEDIATE): %m");
+ Exit(0);
+ }
+
+ /*
+ * Try to enable the chip/driver's multicast address filter to
+ * grab our RMP address. If this fails, try promiscuous mode.
+ * If this fails, there's no way we are going to get any RMP
+ * packets so just exit here.
+ */
+#ifdef MSG_EOR
+ ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2;
+#endif
+ ifr.ifr_addr.sa_family = AF_UNSPEC;
+ memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN);
+ if (ioctl(BpfFd, BIOCPROMISC, (caddr_t)0) < 0) {
+ syslog(LOG_ERR, "bpf: can't set promiscuous mode: %m");
+ Exit(0);
+ }
+
+ /*
+ * Ask BPF how much buffer space it requires and allocate one.
+ */
+ if (ioctl(BpfFd, BIOCGBLEN, (caddr_t)&BpfLen) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCGBLEN): %m");
+ Exit(0);
+ }
+ if (BpfPkt == NULL)
+ BpfPkt = (u_int8_t *)malloc(BpfLen);
+
+ if (BpfPkt == NULL) {
+ syslog(LOG_ERR, "bpf: out of memory (%u bytes for bpfpkt)",
+ BpfLen);
+ Exit(0);
+ }
+
+ /*
+ * Write a little program to snarf RMP Boot packets and stuff
+ * it down BPF's throat (i.e. set up the packet filter).
+ */
+ {
+#define RMP ((struct rmp_packet *)0)
+ static struct bpf_insn bpf_insn[] = {
+ { BPF_LD|BPF_B|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dsap },
+ { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP },
+ { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.cntrl },
+ { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP },
+ { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dxsap },
+ { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP },
+ { BPF_RET|BPF_K, 0, 0, RMP_MAX_PACKET },
+ { BPF_RET|BPF_K, 0, 0, 0x0 }
+ };
+#undef RMP
+ static struct bpf_program bpf_pgm = {
+ sizeof(bpf_insn)/sizeof(bpf_insn[0]), bpf_insn
+ };
+
+ if (ioctl(BpfFd, BIOCSETF, (caddr_t)&bpf_pgm) < 0) {
+ syslog(LOG_ERR, "bpf: ioctl(BIOCSETF): %m");
+ Exit(0);
+ }
+ }
+
+ return(BpfFd);
+}
+
+/*
+** BPF GetIntfName -- Return the name of a network interface attached to
+** the system, or 0 if none can be found. The interface
+** must be configured up; the lowest unit number is
+** preferred; loopback is ignored.
+**
+** Parameters:
+** errmsg - if no network interface found, *errmsg explains why.
+**
+** Returns:
+** A (static) pointer to interface name, or NULL on error.
+**
+** Side Effects:
+** None.
+*/
+char *
+BpfGetIntfName(char **errmsg)
+{
+ struct ifreq ibuf[8], *ifrp, *ifend, *mp;
+ struct ifconf ifc;
+ int fd;
+ int minunit, n;
+ char *cp;
+ static char device[sizeof(ifrp->ifr_name)];
+ static char errbuf[128] = "No Error!";
+
+ if (errmsg != NULL)
+ *errmsg = errbuf;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ (void) strcpy(errbuf, "bpf: socket: %m");
+ return(NULL);
+ }
+ ifc.ifc_len = sizeof ibuf;
+ ifc.ifc_buf = (caddr_t)ibuf;
+
+ if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
+ ifc.ifc_len < sizeof(struct ifreq)) {
+ (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m");
+ return(NULL);
+ }
+ ifrp = ibuf;
+ ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
+
+ mp = NULL;
+ minunit = 666;
+ for (; ifrp < ifend; ++ifrp) {
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) {
+ (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m");
+ return(NULL);
+ }
+
+ /*
+ * If interface is down or this is the loopback interface,
+ * ignore it.
+ */
+ if ((ifrp->ifr_flags & IFF_UP) == 0 ||
+#ifdef IFF_LOOPBACK
+ (ifrp->ifr_flags & IFF_LOOPBACK))
+#else
+ (strcmp(ifrp->ifr_name, "lo0") == 0))
+#endif
+ continue;
+
+ for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp)
+ ;
+ n = atoi(cp);
+ if (n < minunit) {
+ minunit = n;
+ mp = ifrp;
+ }
+ }
+
+ (void) close(fd);
+ if (mp == NULL) {
+ (void) strcpy(errbuf, "bpf: no interfaces found");
+ return(NULL);
+ }
+
+ (void) strcpy(device, mp->ifr_name);
+ return(device);
+}
+
+/*
+** BpfRead -- Read packets from a BPF device and fill in `rconn'.
+**
+** Parameters:
+** rconn - filled in with next packet.
+** doread - is True if we can issue a read() syscall.
+**
+** Returns:
+** True if `rconn' contains a new packet, False otherwise.
+**
+** Side Effects:
+** None.
+*/
+int
+BpfRead(RMPCONN *rconn, int doread)
+{
+ int datlen, caplen, hdrlen;
+ static u_int8_t *bp = NULL, *ep = NULL;
+ int cc;
+
+ /*
+ * The read() may block, or it may return one or more packets.
+ * We let the caller decide whether or not we can issue a read().
+ */
+ if (doread) {
+ if ((cc = read(BpfFd, (char *)BpfPkt, (int)BpfLen)) < 0) {
+ syslog(LOG_ERR, "bpf: read: %m");
+ return(0);
+ } else {
+ bp = BpfPkt;
+ ep = BpfPkt + cc;
+ }
+ }
+
+#define bhp ((struct bpf_hdr *)bp)
+ /*
+ * If there is a new packet in the buffer, stuff it into `rconn'
+ * and return a success indication.
+ */
+ if (bp < ep) {
+ datlen = bhp->bh_datalen;
+ caplen = bhp->bh_caplen;
+ hdrlen = bhp->bh_hdrlen;
+
+ if (caplen != datlen)
+ syslog(LOG_ERR,
+ "bpf: short packet dropped (%d of %d bytes)",
+ caplen, datlen);
+ else if (caplen > sizeof(struct rmp_packet))
+ syslog(LOG_ERR, "bpf: large packet dropped (%d bytes)",
+ caplen);
+ else {
+ rconn->rmplen = caplen;
+ memmove((char *)&rconn->tstamp, (char *)&bhp->bh_tstamp,
+ sizeof(struct timeval));
+ memmove((char *)&rconn->rmp, (char *)bp + hdrlen, caplen);
+ }
+ bp += BPF_WORDALIGN(caplen + hdrlen);
+ return(1);
+ }
+#undef bhp
+
+ return(0);
+}
+
+/*
+** BpfWrite -- Write packet to BPF device.
+**
+** Parameters:
+** rconn - packet to send.
+**
+** Returns:
+** True if write succeeded, False otherwise.
+**
+** Side Effects:
+** None.
+*/
+int
+BpfWrite(RMPCONN *rconn)
+{
+ if (write(BpfFd, (char *)&rconn->rmp, rconn->rmplen) < 0) {
+ syslog(LOG_ERR, "write: %s: %m", EnetStr(rconn));
+ return(0);
+ }
+
+ return(1);
+}
+
+/*
+** BpfClose -- Close a BPF device.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** None.
+*/
+void
+BpfClose(void)
+{
+ struct ifreq ifr;
+
+ if (BpfPkt != NULL) {
+ free((char *)BpfPkt);
+ BpfPkt = NULL;
+ }
+
+ if (BpfFd == -1)
+ return;
+
+#ifdef MSG_EOR
+ ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2;
+#endif
+ ifr.ifr_addr.sa_family = AF_UNSPEC;
+ memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN);
+ if (ioctl(BpfFd, SIOCDELMULTI, (caddr_t)&ifr) < 0)
+ (void) ioctl(BpfFd, BIOCPROMISC, (caddr_t)0);
+
+ (void) close(BpfFd);
+ BpfFd = -1;
+}
diff --git a/libexec/rbootd/conf.c b/libexec/rbootd/conf.c
new file mode 100644
index 000000000000..dc9e3ac6a60d
--- /dev/null
+++ b/libexec/rbootd/conf.c
@@ -0,0 +1,81 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Utah Hdr: conf.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include "defs.h"
+#include "pathnames.h"
+
+/*
+** Define (and possibly initialize) global variables here.
+**
+** Caveat:
+** The maximum number of bootable files (`char *BootFiles[]') is
+** limited to C_MAXFILE (i.e. the maximum number of files that
+** can be spec'd in the configuration file). This was done to
+** simplify the boot file search code.
+*/
+
+char MyHost[MAXHOSTNAMELEN]; /* host name */
+pid_t MyPid; /* process id */
+int DebugFlg = 0; /* set true if debugging */
+int BootAny = 0; /* set true if we boot anyone */
+
+char *ConfigFile = NULL; /* configuration file */
+char *DfltConfig = _PATH_RBOOTDCONF; /* default configuration file */
+char *PidFile = _PATH_RBOOTDPID; /* file w/pid of server */
+char *BootDir = _PATH_RBOOTDLIB; /* directory w/boot files */
+char *DbgFile = _PATH_RBOOTDDBG; /* debug output file */
+
+FILE *DbgFp = NULL; /* debug file pointer */
+char *IntfName = NULL; /* intf we are attached to */
+
+u_int16_t SessionID = 0; /* generated session ID */
+
+char *BootFiles[C_MAXFILE]; /* list of boot files */
+
+CLIENT *Clients = NULL; /* list of addrs we'll accept */
+RMPCONN *RmpConns = NULL; /* list of active connections */
+
+u_int8_t RmpMcastAddr[RMP_ADDRLEN] = RMP_ADDR; /* RMP multicast address */
diff --git a/libexec/rbootd/defs.h b/libexec/rbootd/defs.h
new file mode 100644
index 000000000000..87bbb5716b83
--- /dev/null
+++ b/libexec/rbootd/defs.h
@@ -0,0 +1,180 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Utah Hdr: defs.h 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#include "rmp.h"
+#include "rmp_var.h"
+
+/*
+** Common #define's and external variables. All other files should
+** include this.
+*/
+
+/*
+ * This may be defined in <sys/param.h>, if not, it's defined here.
+ */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+/*
+ * SIGUSR1 and SIGUSR2 are defined in <signal.h> for 4.3BSD systems.
+ */
+#ifndef SIGUSR1
+#define SIGUSR1 SIGEMT
+#endif
+#ifndef SIGUSR2
+#define SIGUSR2 SIGFPE
+#endif
+
+/*
+ * These can be faster & more efficient than strcmp()/strncmp()...
+ */
+#define STREQN(s1,s2) ((*s1 == *s2) && (strcmp(s1,s2) == 0))
+#define STRNEQN(s1,s2,n) ((*s1 == *s2) && (strncmp(s1,s2,n) == 0))
+
+/*
+ * Configuration file limitations.
+ */
+#define C_MAXFILE 10 /* max number of boot-able files */
+#define C_LINELEN 1024 /* max length of line */
+
+/*
+ * Direction of packet (used as argument to DispPkt).
+ */
+#define DIR_RCVD 0
+#define DIR_SENT 1
+#define DIR_NONE 2
+
+/*
+ * These need not be functions, so...
+ */
+#define FreeStr(str) free(str)
+#define FreeClient(cli) free(cli)
+#define GenSessID() (++SessionID ? SessionID: ++SessionID)
+
+/*
+ * Converting an Ethernet address to a string is done in many routines.
+ * Using `rmp.hp_hdr.saddr' works because this field is *never* changed;
+ * it will *always* contain the source address of the packet.
+ */
+#define EnetStr(rptr) GetEtherAddr(&(rptr)->rmp.hp_hdr.saddr[0])
+
+/*
+ * Every machine we can boot will have one of these allocated for it
+ * (unless there are no restrictions on who we can boot).
+ */
+typedef struct client_s {
+ u_int8_t addr[RMP_ADDRLEN]; /* addr of machine */
+ char *files[C_MAXFILE]; /* boot-able files */
+ struct client_s *next; /* ptr to next */
+} CLIENT;
+
+/*
+ * Every active connection has one of these allocated for it.
+ */
+typedef struct rmpconn_s {
+ struct rmp_packet rmp; /* RMP packet */
+ int rmplen; /* length of packet */
+ struct timeval tstamp; /* last time active */
+ int bootfd; /* open boot file */
+ struct rmpconn_s *next; /* ptr to next */
+} RMPCONN;
+
+/*
+ * All these variables are defined in "conf.c".
+ */
+extern char MyHost[]; /* this hosts' name */
+extern pid_t MyPid; /* this processes' ID */
+extern int DebugFlg; /* set true if debugging */
+extern int BootAny; /* set true if we can boot anyone */
+
+extern char *ConfigFile; /* configuration file */
+extern char *DfltConfig; /* default configuration file */
+extern char *DbgFile; /* debug output file */
+extern char *PidFile; /* file containing pid of server */
+extern char *BootDir; /* directory w/boot files */
+
+extern FILE *DbgFp; /* debug file pointer */
+extern char *IntfName; /* interface we are attached to */
+
+extern u_int16_t SessionID; /* generated session ID */
+
+extern char *BootFiles[]; /* list of boot files */
+
+extern CLIENT *Clients; /* list of addrs we'll accept */
+extern RMPCONN *RmpConns; /* list of active connections */
+
+extern u_int8_t RmpMcastAddr[]; /* RMP multicast address */
+
+void AddConn(RMPCONN *);
+int BootDone(RMPCONN *);
+void BpfClose(void);
+char *BpfGetIntfName(char **);
+int BpfOpen(void);
+int BpfRead(RMPCONN *, int);
+int BpfWrite(RMPCONN *);
+void DebugOff(int);
+void DebugOn(int);
+void DispPkt(RMPCONN *, int);
+void DoTimeout(void);
+void DspFlnm(u_int, char *);
+void Exit(int);
+CLIENT *FindClient(RMPCONN *);
+RMPCONN *FindConn(RMPCONN *);
+void FreeClients(void);
+void FreeConn(RMPCONN *);
+void FreeConns(void);
+int GetBootFiles(void);
+char *GetEtherAddr(u_int8_t *);
+CLIENT *NewClient(u_int8_t *);
+RMPCONN *NewConn(RMPCONN *);
+char *NewStr(char *);
+u_int8_t *ParseAddr(char *);
+int ParseConfig(void);
+void ProcessPacket(RMPCONN *, CLIENT *);
+void ReConfig(int);
+void RemoveConn(RMPCONN *);
+int SendBootRepl(struct rmp_packet *, RMPCONN *, char *[]);
+int SendFileNo(struct rmp_packet *, RMPCONN *, char *[]);
+int SendPacket(RMPCONN *);
+int SendReadRepl(RMPCONN *);
+int SendServerID(RMPCONN *);
diff --git a/libexec/rbootd/parseconf.c b/libexec/rbootd/parseconf.c
new file mode 100644
index 000000000000..7a8b1028b497
--- /dev/null
+++ b/libexec/rbootd/parseconf.c
@@ -0,0 +1,350 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Utah Hdr: parseconf.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "defs.h"
+
+/*
+** ParseConfig -- parse the config file into linked list of clients.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** 1 on success, 0 otherwise.
+**
+** Side Effects:
+** - Linked list of clients will be (re)allocated.
+**
+** Warnings:
+** - GetBootFiles() must be called before this routine
+** to create a linked list of default boot files.
+*/
+int
+ParseConfig(void)
+{
+ FILE *fp;
+ CLIENT *client;
+ u_int8_t *addr;
+ char line[C_LINELEN];
+ char *cp, *bcp;
+ int i, j;
+ int omask, linecnt = 0;
+
+ if (BootAny) /* ignore config file */
+ return(1);
+
+ FreeClients(); /* delete old list of clients */
+
+ if ((fp = fopen(ConfigFile, "r")) == NULL) {
+ syslog(LOG_ERR, "ParseConfig: can't open config file (%s)",
+ ConfigFile);
+ return(0);
+ }
+
+ /*
+ * We've got to block SIGHUP to prevent reconfiguration while
+ * dealing with the linked list of Clients. This can be done
+ * when actually linking the new client into the list, but
+ * this could have unexpected results if the server was HUP'd
+ * whilst reconfiguring. Hence, it is done here.
+ */
+ omask = sigblock(sigmask(SIGHUP));
+
+ /*
+ * GETSTR positions `bcp' at the start of the current token,
+ * and null terminates it. `cp' is positioned at the start
+ * of the next token. spaces & commas are separators.
+ */
+#define GETSTR while (isspace(*cp) || *cp == ',') cp++; \
+ bcp = cp; \
+ while (*cp && *cp!=',' && !isspace(*cp)) cp++; \
+ if (*cp) *cp++ = '\0'
+
+ /*
+ * For each line, parse it into a new CLIENT struct.
+ */
+ while (fgets(line, C_LINELEN, fp) != NULL) {
+ linecnt++; /* line counter */
+
+ if (*line == '\0' || *line == '#') /* ignore comment */
+ continue;
+
+ if ((cp = strchr(line,'#')) != NULL) /* trash comments */
+ *cp = '\0';
+
+ cp = line; /* init `cp' */
+ GETSTR; /* get RMP addr */
+ if (bcp == cp) /* all delimiters */
+ continue;
+
+ /*
+ * Get an RMP address from a string. Abort on failure.
+ */
+ if ((addr = ParseAddr(bcp)) == NULL) {
+ syslog(LOG_ERR,
+ "ParseConfig: line %d: can't parse <%s>",
+ linecnt, bcp);
+ continue;
+ }
+
+ if ((client = NewClient(addr)) == NULL) /* alloc new client */
+ continue;
+
+ GETSTR; /* get first file */
+
+ /*
+ * If no boot files are spec'd, use the default list.
+ * Otherwise, validate each file (`bcp') against the
+ * list of boot-able files.
+ */
+ i = 0;
+ if (bcp == cp) /* no files spec'd */
+ for (; i < C_MAXFILE && BootFiles[i] != NULL; i++)
+ client->files[i] = BootFiles[i];
+ else {
+ do {
+ /*
+ * For each boot file spec'd, make sure it's
+ * in our list. If so, include a pointer to
+ * it in the CLIENT's list of boot files.
+ */
+ for (j = 0; ; j++) {
+ if (j==C_MAXFILE||BootFiles[j]==NULL) {
+ syslog(LOG_ERR, "ParseConfig: line %d: no boot file (%s)",
+ linecnt, bcp);
+ break;
+ }
+ if (STREQN(BootFiles[j], bcp)) {
+ if (i < C_MAXFILE)
+ client->files[i++] =
+ BootFiles[j];
+ else
+ syslog(LOG_ERR, "ParseConfig: line %d: too many boot files (%s)",
+ linecnt, bcp);
+ break;
+ }
+ }
+ GETSTR; /* get next file */
+ } while (bcp != cp);
+
+ /*
+ * Restricted list of boot files were spec'd,
+ * however, none of them were found. Since we
+ * apparently can't let them boot "just anything",
+ * the entire record is invalidated.
+ */
+ if (i == 0) {
+ FreeClient(client);
+ continue;
+ }
+ }
+
+ /*
+ * Link this client into the linked list of clients.
+ * SIGHUP has already been blocked.
+ */
+ if (Clients)
+ client->next = Clients;
+ Clients = client;
+ }
+
+ (void) fclose(fp); /* close config file */
+
+ (void) sigsetmask(omask); /* reset signal mask */
+
+ return(1); /* return success */
+}
+
+/*
+** ParseAddr -- Parse a string containing an RMP address.
+**
+** This routine is fairly liberal at parsing an RMP address. The
+** address must contain 6 octets consisting of between 0 and 2 hex
+** chars (upper/lower case) separated by colons. If two colons are
+** together (e.g. "::", the octet between them is recorded as being
+** zero. Hence, the following addrs are all valid and parse to the
+** same thing:
+**
+** 08:00:09:00:66:ad 8::9:0:66:AD 8::9::66:aD
+**
+** For clarity, an RMP address is really an Ethernet address, but
+** since the HP boot code uses IEEE 802.3, it's really an IEEE
+** 802.3 address. Of course, all of these are identical.
+**
+** Parameters:
+** str - string representation of an RMP address.
+**
+** Returns:
+** pointer to a static array of RMP_ADDRLEN bytes.
+**
+** Side Effects:
+** None.
+**
+** Warnings:
+** - The return value points to a static buffer; it must
+** be copied if it's to be saved.
+*/
+u_int8_t *
+ParseAddr(char *str)
+{
+ static u_int8_t addr[RMP_ADDRLEN];
+ char *cp;
+ unsigned i;
+ int part, subpart;
+
+ memset((char *)&addr[0], 0, RMP_ADDRLEN); /* zero static buffer */
+
+ part = subpart = 0;
+ for (cp = str; *cp; cp++) {
+ /*
+ * A colon (`:') must be used to delimit each octet.
+ */
+ if (*cp == ':') {
+ if (++part == RMP_ADDRLEN) /* too many parts */
+ return(NULL);
+ subpart = 0;
+ continue;
+ }
+
+ /*
+ * Convert hex character to an integer.
+ */
+ if (isdigit(*cp))
+ i = *cp - '0';
+ else {
+ i = (isupper(*cp)? tolower(*cp): *cp) - 'a' + 10;
+ if (i < 10 || i > 15) /* not a hex char */
+ return(NULL);
+ }
+
+ if (subpart++) {
+ if (subpart > 2) /* too many hex chars */
+ return(NULL);
+ addr[part] <<= 4;
+ }
+ addr[part] |= i;
+ }
+
+ if (part != (RMP_ADDRLEN-1)) /* too few parts */
+ return(NULL);
+
+ return(&addr[0]);
+}
+
+/*
+** GetBootFiles -- record list of files in current (boot) directory.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Number of boot files on success, 0 on failure.
+**
+** Side Effects:
+** Strings in `BootFiles' are freed/allocated.
+**
+** Warnings:
+** - After this routine is called, ParseConfig() must be
+** called to re-order it's list of boot file pointers.
+*/
+int
+GetBootFiles(void)
+{
+ DIR *dfd;
+ struct stat statb;
+ struct dirent *dp;
+ int i;
+
+ /*
+ * Free the current list of boot files.
+ */
+ for (i = 0; i < C_MAXFILE && BootFiles[i] != NULL; i++) {
+ FreeStr(BootFiles[i]);
+ BootFiles[i] = NULL;
+ }
+
+ /*
+ * Open current directory to read boot file names.
+ */
+ if ((dfd = opendir(".")) == NULL) { /* open BootDir */
+ syslog(LOG_ERR, "GetBootFiles: can't open directory (%s)\n",
+ BootDir);
+ return(0);
+ }
+
+ /*
+ * Read each boot file name and allocate space for it in the
+ * list of boot files (BootFiles). All boot files read after
+ * C_MAXFILE will be ignored.
+ */
+ i = 0;
+ for (dp = readdir(dfd); dp != NULL; dp = readdir(dfd)) {
+ if (stat(dp->d_name, &statb) < 0 ||
+ (statb.st_mode & S_IFMT) != S_IFREG)
+ continue;
+ if (i == C_MAXFILE)
+ syslog(LOG_ERR,
+ "GetBootFiles: too many boot files (%s ignored)",
+ dp->d_name);
+ else if ((BootFiles[i] = NewStr(dp->d_name)) != NULL)
+ i++;
+ }
+
+ (void) closedir(dfd); /* close BootDir */
+
+ if (i == 0) /* can't find any boot files */
+ syslog(LOG_ERR, "GetBootFiles: no boot files (%s)\n", BootDir);
+
+ return(i);
+}
diff --git a/libexec/rbootd/pathnames.h b/libexec/rbootd/pathnames.h
new file mode 100644
index 000000000000..eed4e6db535c
--- /dev/null
+++ b/libexec/rbootd/pathnames.h
@@ -0,0 +1,47 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Utah Hdr: pathnames.h 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#define _PATH_BPF "/dev/bpf%d"
+#define _PATH_RBOOTDCONF "/etc/rbootd.conf"
+#define _PATH_RBOOTDDBG "/tmp/rbootd.dbg"
+#define _PATH_RBOOTDLIB "/usr/mdec/rbootd"
+#define _PATH_RBOOTDPID "/var/run/rbootd.pid"
diff --git a/libexec/rbootd/rbootd.8 b/libexec/rbootd/rbootd.8
new file mode 100644
index 000000000000..592c5fcb2f99
--- /dev/null
+++ b/libexec/rbootd/rbootd.8
@@ -0,0 +1,150 @@
+.\" Copyright (c) 1988, 1992 The University of Utah and the Center
+.\" for Software Science (CSS).
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Center for Software Science of the University of Utah Computer
+.\" Science Department. CSS requests users of this software to return
+.\" to css-dist@cs.utah.edu any improvements that they make and grant
+.\" CSS redistribution rights.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" Utah Hdr: rbootd.man 3.1 92/07/06
+.\" Author: Jeff Forys, University of Utah CSS
+.\"
+.Dd December 11, 1993
+.Dt RBOOTD 8
+.Os
+.Sh NAME
+.Nm rbootd
+.Nd HP remote boot server
+.Sh SYNOPSIS
+.Nm
+.Op Fl ad
+.Op Fl i Ar interface
+.Op config_file
+.Sh DESCRIPTION
+The
+.Nm
+utility services boot requests from Hewlett-Packard workstations over a
+local area network.
+All boot files must reside in the boot file directory; further, if a
+client supplies path information in its boot request, it will be silently
+stripped away before processing.
+By default,
+.Nm
+only responds to requests from machines listed in its configuration file.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Respond to boot requests from any machine.
+The configuration file is ignored if this option is specified.
+.It Fl d
+Run
+.Nm
+in debug mode.
+Packets sent and received are displayed to the terminal.
+.It Fl i Ar interface
+Service boot requests on specified interface.
+If unspecified,
+.Nm
+searches the system interface list for the lowest numbered, configured
+``up'' interface (excluding loopback).
+Ties are broken by choosing the earliest match.
+.El
+.Pp
+Specifying
+.Ar config_file
+on the command line causes
+.Nm
+to use a different configuration file from the default.
+.Pp
+The configuration file is a text file where each line describes a particular
+machine.
+A line must start with a machine's Ethernet address followed by an optional
+list of boot file names.
+An Ethernet address is specified in hexadecimal with each of its six octets
+separated by a colon.
+The boot file names come from the boot file directory.
+The ethernet address and boot file(s) must be separated by white-space
+and/or comma characters.
+A pound sign causes the remainder of a line to be ignored.
+.Pp
+Here is a sample configuration file:
+.Bd -literal
+#
+# ethernet addr boot file(s) comments
+#
+08:00:09:0:66:ad SYSHPBSD # snake (4.3BSD)
+08:00:09:0:59:5b # vandy (anything)
+8::9:1:C6:75 SYSHPBSD,SYSHPUX # jaguar (either)
+.Ed
+.Pp
+The
+.Nm
+utility logs status and error messages via
+.Xr syslog 3 .
+A startup message is always logged, and in the case of fatal errors (or
+deadly signals) a message is logged announcing the server's termination.
+In general, a non-fatal error is handled by ignoring the event that caused
+it (e.g.\& an invalid Ethernet address in the config file causes that line
+to be invalidated).
+.Pp
+The following signals have the specified effect when sent to the server
+process using the
+.Xr kill 1
+command:
+.Bl -tag -width SIGUSR1 -offset xxxxxxxx
+.It SIGHUP
+Drop all active connections and reconfigure.
+.It SIGUSR1
+Turn on debugging, do nothing if already on.
+.It SIGUSR2
+Turn off debugging, do nothing if already off.
+.El
+.Sh "FILES"
+.Bl -tag -width /usr/libexec/rbootd -compact
+.It Pa /dev/bpf#
+packet-filter device
+.It Pa /etc/rbootd.conf
+configuration file
+.It Pa /tmp/rbootd.dbg
+debug output
+.It Pa /usr/mdec/rbootd
+directory containing boot files
+.It Pa /var/run/rbootd.pid
+process id
+.El
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr socket 2 ,
+.Xr signal 3 ,
+.Xr syslog 3
+.Sh BUGS
+If multiple servers are started on the same interface, each will receive
+and respond to the same boot packets.
diff --git a/libexec/rbootd/rbootd.c b/libexec/rbootd/rbootd.c
new file mode 100644
index 000000000000..4eb945741a0d
--- /dev/null
+++ b/libexec/rbootd/rbootd.c
@@ -0,0 +1,435 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Utah Hdr: rbootd.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "defs.h"
+
+static void usage(void) __dead2;
+
+int
+main(int argc, char *argv[])
+{
+ int c, fd, omask, maxfds;
+ fd_set rset;
+
+ /*
+ * Close any open file descriptors.
+ * Temporarily leave stdin & stdout open for `-d',
+ * and stderr open for any pre-syslog error messages.
+ */
+ {
+ int i, nfds = getdtablesize();
+
+ for (i = 0; i < nfds; i++)
+ if (i != fileno(stdin) && i != fileno(stdout) &&
+ i != fileno(stderr))
+ (void) close(i);
+ }
+
+ /*
+ * Parse any arguments.
+ */
+ while ((c = getopt(argc, argv, "adi:")) != -1)
+ switch(c) {
+ case 'a':
+ BootAny++;
+ break;
+ case 'd':
+ DebugFlg++;
+ break;
+ case 'i':
+ IntfName = optarg;
+ break;
+ default:
+ usage();
+ }
+ for (; optind < argc; optind++) {
+ if (ConfigFile == NULL)
+ ConfigFile = argv[optind];
+ else {
+ warnx("too many config files (`%s' ignored)",
+ argv[optind]);
+ }
+ }
+
+ if (ConfigFile == NULL) /* use default config file */
+ ConfigFile = DfltConfig;
+
+ if (DebugFlg) {
+ DbgFp = stdout; /* output to stdout */
+
+ (void) signal(SIGUSR1, SIG_IGN); /* dont muck w/DbgFp */
+ (void) signal(SIGUSR2, SIG_IGN);
+ (void) fclose(stderr); /* finished with it */
+ } else {
+ if (daemon(0, 0))
+ err(1, "can't detach from terminal");
+
+ (void) signal(SIGUSR1, DebugOn);
+ (void) signal(SIGUSR2, DebugOff);
+ }
+
+ openlog("rbootd", LOG_PID, LOG_DAEMON);
+
+ /*
+ * If no interface was specified, get one now.
+ *
+ * This is convoluted because we want to get the default interface
+ * name for the syslog("restarted") message. If BpfGetIntfName()
+ * runs into an error, it will return a syslog-able error message
+ * (in `errmsg') which will be displayed here.
+ */
+ if (IntfName == NULL) {
+ char *errmsg;
+
+ if ((IntfName = BpfGetIntfName(&errmsg)) == NULL) {
+ /* Backslash to avoid trigraph '??)'. */
+ syslog(LOG_NOTICE, "restarted (?\?)");
+ /* BpfGetIntfName() returns safe names, using %m */
+ syslog(LOG_ERR, "%s", errmsg);
+ Exit(0);
+ }
+ }
+
+ syslog(LOG_NOTICE, "restarted (%s)", IntfName);
+
+ (void) signal(SIGHUP, ReConfig);
+ (void) signal(SIGINT, Exit);
+ (void) signal(SIGTERM, Exit);
+
+ /*
+ * Grab our host name and pid.
+ */
+ if (gethostname(MyHost, MAXHOSTNAMELEN - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ Exit(0);
+ }
+ MyHost[MAXHOSTNAMELEN - 1] = '\0';
+
+ MyPid = getpid();
+
+ /*
+ * Write proc's pid to a file.
+ */
+ {
+ FILE *fp;
+
+ if ((fp = fopen(PidFile, "w")) != NULL) {
+ (void) fprintf(fp, "%d\n", (int) MyPid);
+ (void) fclose(fp);
+ } else {
+ syslog(LOG_WARNING, "fopen: failed (%s)", PidFile);
+ }
+ }
+
+ /*
+ * All boot files are relative to the boot directory, we might
+ * as well chdir() there to make life easier.
+ */
+ if (chdir(BootDir) < 0) {
+ syslog(LOG_ERR, "chdir: %m (%s)", BootDir);
+ Exit(0);
+ }
+
+ /*
+ * Initial configuration.
+ */
+ omask = sigblock(sigmask(SIGHUP)); /* prevent reconfig's */
+ if (GetBootFiles() == 0) /* get list of boot files */
+ Exit(0);
+ if (ParseConfig() == 0) /* parse config file */
+ Exit(0);
+
+ /*
+ * Open and initialize a BPF device for the appropriate interface.
+ * If an error is encountered, a message is displayed and Exit()
+ * is called.
+ */
+ fd = BpfOpen();
+
+ (void) sigsetmask(omask); /* allow reconfig's */
+
+ /*
+ * Main loop: receive a packet, determine where it came from,
+ * and if we service this host, call routine to handle request.
+ */
+ maxfds = fd + 1;
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+ for (;;) {
+ struct timeval timeout;
+ fd_set r;
+ int nsel;
+
+ r = rset;
+
+ if (RmpConns == NULL) { /* timeout isn't necessary */
+ nsel = select(maxfds, &r, NULL, NULL, NULL);
+ } else {
+ timeout.tv_sec = RMP_TIMEOUT;
+ timeout.tv_usec = 0;
+ nsel = select(maxfds, &r, NULL, NULL, &timeout);
+ }
+
+ if (nsel < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: %m");
+ Exit(0);
+ } else if (nsel == 0) { /* timeout */
+ DoTimeout(); /* clear stale conns */
+ continue;
+ }
+
+ if (FD_ISSET(fd, &r)) {
+ RMPCONN rconn;
+ CLIENT *client;
+ int doread = 1;
+
+ while (BpfRead(&rconn, doread)) {
+ doread = 0;
+
+ if (DbgFp != NULL) /* display packet */
+ DispPkt(&rconn,DIR_RCVD);
+
+ omask = sigblock(sigmask(SIGHUP));
+
+ /*
+ * If we do not restrict service, set the
+ * client to NULL (ProcessPacket() handles
+ * this). Otherwise, check that we can
+ * service this host; if not, log a message
+ * and ignore the packet.
+ */
+ if (BootAny) {
+ client = NULL;
+ } else if ((client=FindClient(&rconn))==NULL) {
+ syslog(LOG_INFO,
+ "%s: boot packet ignored",
+ EnetStr(&rconn));
+ (void) sigsetmask(omask);
+ continue;
+ }
+
+ ProcessPacket(&rconn,client);
+
+ (void) sigsetmask(omask);
+ }
+ }
+ }
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: rbootd [-ad] [-i interface] [config_file]\n");
+ exit (1);
+}
+
+/*
+** DoTimeout -- Free any connections that have timed out.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Timed out connections in `RmpConns' will be freed.
+*/
+void
+DoTimeout(void)
+{
+ RMPCONN *rtmp;
+ time_t now;
+
+ /*
+ * For each active connection, if RMP_TIMEOUT seconds have passed
+ * since the last packet was sent, delete the connection.
+ */
+ now = time(NULL);
+ for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next)
+ if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now) {
+ syslog(LOG_WARNING, "%s: connection timed out (%u)",
+ EnetStr(rtmp), rtmp->rmp.r_type);
+ RemoveConn(rtmp);
+ }
+}
+
+/*
+** FindClient -- Find client associated with a packet.
+**
+** Parameters:
+** rconn - the new packet.
+**
+** Returns:
+** Pointer to client info if found, NULL otherwise.
+**
+** Side Effects:
+** None.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked since
+** a reconfigure can invalidate the information returned.
+*/
+
+CLIENT *
+FindClient(RMPCONN *rconn)
+{
+ CLIENT *ctmp;
+
+ for (ctmp = Clients; ctmp != NULL; ctmp = ctmp->next)
+ if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0],
+ (char *)&ctmp->addr[0], RMP_ADDRLEN) == 0)
+ break;
+
+ return(ctmp);
+}
+
+/*
+** Exit -- Log an error message and exit.
+**
+** Parameters:
+** sig - caught signal (or zero if not dying on a signal).
+**
+** Returns:
+** Does not return.
+**
+** Side Effects:
+** - This process ceases to exist.
+*/
+void
+Exit(int sig)
+{
+ if (sig > 0)
+ syslog(LOG_ERR, "going down on signal %d", sig);
+ else
+ syslog(LOG_ERR, "going down with fatal error");
+ BpfClose();
+ exit(1);
+}
+
+/*
+** ReConfig -- Get new list of boot files and reread config files.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - All active connections are dropped.
+** - List of boot-able files is changed.
+** - List of clients is changed.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+ReConfig(int signo __unused)
+{
+ syslog(LOG_NOTICE, "reconfiguring boot server");
+
+ FreeConns();
+
+ if (GetBootFiles() == 0)
+ Exit(0);
+
+ if (ParseConfig() == 0)
+ Exit(0);
+}
+
+/*
+** DebugOff -- Turn off debugging.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Debug file is closed.
+*/
+void
+DebugOff(int signo __unused)
+{
+ if (DbgFp != NULL)
+ (void) fclose(DbgFp);
+
+ DbgFp = NULL;
+}
+
+/*
+** DebugOn -- Turn on debugging.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Debug file is opened/truncated if not already opened,
+** otherwise do nothing.
+*/
+void
+DebugOn(int signo __unused)
+{
+ if (DbgFp == NULL) {
+ if ((DbgFp = fopen(DbgFile, "w")) == NULL)
+ syslog(LOG_ERR, "can't open debug file (%s)", DbgFile);
+ }
+}
diff --git a/libexec/rbootd/rmp.h b/libexec/rbootd/rmp.h
new file mode 100644
index 000000000000..bc739b751afc
--- /dev/null
+++ b/libexec/rbootd/rmp.h
@@ -0,0 +1,91 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Utah Hdr: rmp.h 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+/*
+ * Define MIN/MAX sizes of RMP (ethernet) packet.
+ * For ease of computation, the 4 octet CRC field is not included.
+ *
+ * MCLBYTES is for bpfwrite(); it is adamant about using a cluster.
+ */
+
+#define RMP_MAX_PACKET MIN(1514,MCLBYTES)
+#define RMP_MIN_PACKET 60
+
+/*
+ * Define RMP/Ethernet Multicast address (9:0:9:0:0:4) and its length.
+ */
+#define RMP_ADDR { 0x9, 0x0, 0x9, 0x0, 0x0, 0x4 }
+#define RMP_ADDRLEN 6
+
+/*
+ * Define IEEE802.2 (Logical Link Control) information.
+ */
+#define IEEE_DSAP_HP 0xF8 /* Destination Service Access Point */
+#define IEEE_SSAP_HP 0xF8 /* Source Service Access Point */
+#define IEEE_CNTL_HP 0x0300 /* Type 1 / I format control information */
+
+#define HPEXT_DXSAP 0x608 /* HP Destination Service Access Point */
+#define HPEXT_SXSAP 0x609 /* HP Source Service Access Point */
+
+/*
+ * 802.3-style "Ethernet" header.
+ */
+
+struct hp_hdr {
+ u_int8_t daddr[RMP_ADDRLEN];
+ u_int8_t saddr[RMP_ADDRLEN];
+ u_int16_t len;
+};
+
+/*
+ * HP uses 802.2 LLC with their own local extensions. This struct makes
+ * sense out of this data (encapsulated in the above 802.3 packet).
+ */
+
+struct hp_llc {
+ u_int8_t dsap; /* 802.2 DSAP */
+ u_int8_t ssap; /* 802.2 SSAP */
+ u_int16_t cntrl; /* 802.2 control field */
+ u_int16_t filler; /* HP filler (must be zero) */
+ u_int16_t dxsap; /* HP extended DSAP */
+ u_int16_t sxsap; /* HP extended SSAP */
+};
diff --git a/libexec/rbootd/rmp_var.h b/libexec/rbootd/rmp_var.h
new file mode 100644
index 000000000000..0d3003edd565
--- /dev/null
+++ b/libexec/rbootd/rmp_var.h
@@ -0,0 +1,240 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah Hdr: rmp_var.h 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+/*
+ * Possible values for "rmp_type" fields.
+ */
+
+#define RMP_BOOT_REQ 1 /* boot request packet */
+#define RMP_BOOT_REPL 129 /* boot reply packet */
+#define RMP_READ_REQ 2 /* read request packet */
+#define RMP_READ_REPL 130 /* read reply packet */
+#define RMP_BOOT_DONE 3 /* boot complete packet */
+
+/*
+ * Useful constants.
+ */
+
+#define RMP_VERSION 2 /* protocol version */
+#define RMP_TIMEOUT 600 /* timeout connection after ten minutes */
+#define RMP_PROBESID 0xffff /* session ID for probes */
+#define RMP_HOSTLEN 13 /* max length of server's name */
+#define RMP_MACHLEN 20 /* length of machine type field */
+
+/*
+ * RMP error codes
+ */
+
+#define RMP_E_OKAY 0
+#define RMP_E_EOF 2 /* read reply: returned end of file */
+#define RMP_E_ABORT 3 /* abort operation */
+#define RMP_E_BUSY 4 /* boot reply: server busy */
+#define RMP_E_TIMEOUT 5 /* lengthen time out (not implemented) */
+#define RMP_E_NOFILE 16 /* boot reply: file does not exist */
+#define RMP_E_OPENFILE 17 /* boot reply: file open failed */
+#define RMP_E_NODFLT 18 /* boot reply: default file does not exist */
+#define RMP_E_OPENDFLT 19 /* boot reply: default file open failed */
+#define RMP_E_BADSID 25 /* read reply: bad session ID */
+#define RMP_E_BADPACKET 27 /* Bad packet detected */
+
+/*
+ * RMPDATALEN is the maximum number of data octets that can be stuffed
+ * into an RMP packet. This excludes the 802.2 LLC w/HP extensions.
+ */
+#define RMPDATALEN (RMP_MAX_PACKET - (sizeof(struct hp_hdr) + \
+ sizeof(struct hp_llc)))
+
+/*
+ * Define sizes of packets we send. Boot and Read replies are variable
+ * in length depending on the length of `s'.
+ *
+ * Also, define how much space `restofpkt' can take up for outgoing
+ * Boot and Read replies. Boot Request packets are effectively
+ * limited to 255 bytes due to the preceding 1-byte length field.
+ */
+
+#define RMPBOOTSIZE(s) (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \
+ sizeof(struct rmp_boot_repl) + s - sizeof(restofpkt))
+#define RMPREADSIZE(s) (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \
+ sizeof(struct rmp_read_repl) + s - sizeof(restofpkt) \
+ - sizeof(u_int8_t))
+#define RMPDONESIZE (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \
+ sizeof(struct rmp_boot_done))
+#define RMPBOOTDATA 255
+#define RMPREADDATA (RMPDATALEN - \
+ (2*sizeof(u_int8_t)+sizeof(u_int16_t)+sizeof(u_word)))
+
+/*
+ * This protocol defines some field sizes as "rest of ethernet packet".
+ * There is no easy way to specify this in C, so we use a one character
+ * field to denote it, and index past it to the end of the packet.
+ */
+
+typedef char restofpkt;
+
+/*
+ * Due to the RMP packet layout, we'll run into alignment problems
+ * on machines that can't access (or don't, by default, align) words
+ * on half-word boundaries. If you know that your machine does not suffer
+ * from this problem, add it to the vax/tahoe/m68k #define below.
+ *
+ * The following macros are used to deal with this problem:
+ * WORDZE(w) Return True if u_word `w' is zero, False otherwise.
+ * ZEROWORD(w) Set u_word `w' to zero.
+ * COPYWORD(w1,w2) Copy u_word `w1' to `w2'.
+ * GETWORD(w,i) Copy u_word `w' into int `i'.
+ * PUTWORD(i,w) Copy int `i' into u_word `w'.
+ *
+ * N.B. Endianness is handled by use of ntohl/htonl
+ */
+#if defined(__vax__) || defined(__tahoe__) || defined(__m68k__)
+
+typedef u_int32_t u_word;
+
+#define WORDZE(w) ((w) == 0)
+#define ZEROWORD(w) (w) = 0
+#define COPYWORD(w1,w2) (w2) = (w1)
+#define GETWORD(w, i) (i) = ntohl(w)
+#define PUTWORD(i, w) (w) = htonl(i)
+
+#else
+
+#define _WORD_HIGHPART 0
+#define _WORD_LOWPART 1
+
+typedef struct _uword { u_int16_t val[2]; } u_word;
+
+#define WORDZE(w) \
+ ((w.val[_WORD_HIGHPART] == 0) && (w.val[_WORD_LOWPART] == 0))
+#define ZEROWORD(w) \
+ (w).val[_WORD_HIGHPART] = (w).val[_WORD_LOWPART] = 0
+#define COPYWORD(w1, w2) \
+ { (w2).val[_WORD_HIGHPART] = (w1).val[_WORD_HIGHPART]; \
+ (w2).val[_WORD_LOWPART] = (w1).val[_WORD_LOWPART]; \
+ }
+#define GETWORD(w, i) \
+ (i) = (((u_int32_t)ntohs((w).val[_WORD_HIGHPART])) << 16) | ntohs((w).val[_WORD_LOWPART])
+#define PUTWORD(i, w) \
+ { (w).val[_WORD_HIGHPART] = htons((u_int16_t) ((i >> 16) & 0xffff)); \
+ (w).val[_WORD_LOWPART] = htons((u_int16_t) (i & 0xffff)); \
+ }
+
+#endif
+
+/*
+ * Packet structures.
+ */
+
+struct rmp_raw { /* generic RMP packet */
+ u_int8_t rmp_type; /* packet type */
+ u_int8_t rmp_rawdata[RMPDATALEN-1];
+};
+
+struct rmp_boot_req { /* boot request */
+ u_int8_t rmp_type; /* packet type (RMP_BOOT_REQ) */
+ u_int8_t rmp_retcode; /* return code (0) */
+ u_word rmp_seqno; /* sequence number (real time clock) */
+ u_int16_t rmp_session; /* session id (normally 0) */
+ u_int16_t rmp_version; /* protocol version (RMP_VERSION) */
+ char rmp_machtype[RMP_MACHLEN]; /* machine type */
+ u_int8_t rmp_flnmsize; /* length of rmp_flnm */
+ restofpkt rmp_flnm; /* name of file to be read */
+};
+
+struct rmp_boot_repl { /* boot reply */
+ u_int8_t rmp_type; /* packet type (RMP_BOOT_REPL) */
+ u_int8_t rmp_retcode; /* return code (normally 0) */
+ u_word rmp_seqno; /* sequence number (from boot req) */
+ u_int16_t rmp_session; /* session id (generated) */
+ u_int16_t rmp_version; /* protocol version (RMP_VERSION) */
+ u_int8_t rmp_flnmsize; /* length of rmp_flnm */
+ restofpkt rmp_flnm; /* name of file (from boot req) */
+};
+
+struct rmp_read_req { /* read request */
+ u_int8_t rmp_type; /* packet type (RMP_READ_REQ) */
+ u_int8_t rmp_retcode; /* return code (0) */
+ u_word rmp_offset; /* file relative byte offset */
+ u_int16_t rmp_session; /* session id (from boot repl) */
+ u_int16_t rmp_size; /* max no of bytes to send */
+};
+
+struct rmp_read_repl { /* read reply */
+ u_int8_t rmp_type; /* packet type (RMP_READ_REPL) */
+ u_int8_t rmp_retcode; /* return code (normally 0) */
+ u_word rmp_offset; /* byte offset (from read req) */
+ u_int16_t rmp_session; /* session id (from read req) */
+ restofpkt rmp_data; /* data (max size from read req) */
+ u_int8_t rmp_unused; /* padding to 16-bit boundary */
+};
+
+struct rmp_boot_done { /* boot complete */
+ u_int8_t rmp_type; /* packet type (RMP_BOOT_DONE) */
+ u_int8_t rmp_retcode; /* return code (0) */
+ u_word rmp_unused; /* not used (0) */
+ u_int16_t rmp_session; /* session id (from read repl) */
+};
+
+struct rmp_packet {
+ struct hp_hdr hp_hdr;
+ struct hp_llc hp_llc;
+ union {
+ struct rmp_boot_req rmp_brq; /* boot request */
+ struct rmp_boot_repl rmp_brpl; /* boot reply */
+ struct rmp_read_req rmp_rrq; /* read request */
+ struct rmp_read_repl rmp_rrpl; /* read reply */
+ struct rmp_boot_done rmp_done; /* boot complete */
+ struct rmp_raw rmp_raw; /* raw data */
+ } rmp_proto;
+};
+
+/*
+ * Make life easier...
+ */
+
+#define r_type rmp_proto.rmp_raw.rmp_type
+#define r_data rmp_proto.rmp_raw.rmp_rawdata
+#define r_brq rmp_proto.rmp_brq
+#define r_brpl rmp_proto.rmp_brpl
+#define r_rrq rmp_proto.rmp_rrq
+#define r_rrpl rmp_proto.rmp_rrpl
+#define r_done rmp_proto.rmp_done
diff --git a/libexec/rbootd/rmpproto.c b/libexec/rbootd/rmpproto.c
new file mode 100644
index 000000000000..8f431255d85b
--- /dev/null
+++ b/libexec/rbootd/rmpproto.c
@@ -0,0 +1,576 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Utah Hdr: rmpproto.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "defs.h"
+
+/*
+** ProcessPacket -- determine packet type and do what's required.
+**
+** An RMP BOOT packet has been received. Look at the type field
+** and process Boot Requests, Read Requests, and Boot Complete
+** packets. Any other type will be dropped with a warning msg.
+**
+** Parameters:
+** rconn - the new connection
+** client - list of files available to this host
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - If this is a valid boot request, it will be added to
+** the linked list of outstanding requests (RmpConns).
+** - If this is a valid boot complete, its associated
+** entry in RmpConns will be deleted.
+** - Also, unless we run out of memory, a reply will be
+** sent to the host that sent the packet.
+*/
+void
+ProcessPacket(RMPCONN *rconn, CLIENT *client)
+{
+ struct rmp_packet *rmp;
+ RMPCONN *rconnout;
+
+ rmp = &rconn->rmp; /* cache pointer to RMP packet */
+
+ switch(rmp->r_type) { /* do what we came here to do */
+ case RMP_BOOT_REQ: /* boot request */
+ if ((rconnout = NewConn(rconn)) == NULL)
+ return;
+
+ /*
+ * If the Session ID is 0xffff, this is a "probe"
+ * packet and we do not want to add the connection
+ * to the linked list of active connections. There
+ * are two types of probe packets, if the Sequence
+ * Number is 0 they want to know our host name, o/w
+ * they want the name of the file associated with
+ * the number spec'd by the Sequence Number.
+ *
+ * If this is an actual boot request, open the file
+ * and send a reply. If SendBootRepl() does not
+ * return 0, add the connection to the linked list
+ * of active connections, otherwise delete it since
+ * an error was encountered.
+ */
+ if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) {
+ if (WORDZE(rmp->r_brq.rmp_seqno))
+ (void) SendServerID(rconnout);
+ else
+ (void) SendFileNo(rmp, rconnout,
+ client? client->files:
+ BootFiles);
+ FreeConn(rconnout);
+ } else {
+ if (SendBootRepl(rmp, rconnout,
+ client? client->files: BootFiles))
+ AddConn(rconnout);
+ else
+ FreeConn(rconnout);
+ }
+ break;
+
+ case RMP_BOOT_REPL: /* boot reply (not valid) */
+ syslog(LOG_WARNING, "%s: sent a boot reply",
+ EnetStr(rconn));
+ break;
+
+ case RMP_READ_REQ: /* read request */
+ /*
+ * Send a portion of the boot file.
+ */
+ (void) SendReadRepl(rconn);
+ break;
+
+ case RMP_READ_REPL: /* read reply (not valid) */
+ syslog(LOG_WARNING, "%s: sent a read reply",
+ EnetStr(rconn));
+ break;
+
+ case RMP_BOOT_DONE: /* boot complete */
+ /*
+ * Remove the entry from the linked list of active
+ * connections.
+ */
+ (void) BootDone(rconn);
+ break;
+
+ default: /* unknown RMP packet type */
+ syslog(LOG_WARNING, "%s: unknown packet type (%u)",
+ EnetStr(rconn), rmp->r_type);
+ }
+}
+
+/*
+** SendServerID -- send our host name to who ever requested it.
+**
+** Parameters:
+** rconn - the reply packet to be formatted.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendServerID(RMPCONN *rconn)
+{
+ struct rmp_packet *rpl;
+ char *src, *dst;
+ u_int8_t *size;
+
+ rpl = &rconn->rmp; /* cache ptr to RMP packet */
+
+ /*
+ * Set up assorted fields in reply packet.
+ */
+ rpl->r_brpl.rmp_type = RMP_BOOT_REPL;
+ rpl->r_brpl.rmp_retcode = RMP_E_OKAY;
+ ZEROWORD(rpl->r_brpl.rmp_seqno);
+ rpl->r_brpl.rmp_session = 0;
+ rpl->r_brpl.rmp_version = htons(RMP_VERSION);
+
+ size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */
+
+ /*
+ * Copy our host name into the reply packet incrementing the
+ * length as we go. Stop at RMP_HOSTLEN or the first dot.
+ */
+ src = MyHost;
+ dst = (char *) &rpl->r_brpl.rmp_flnm;
+ for (*size = 0; *size < RMP_HOSTLEN; (*size)++) {
+ if (*src == '.' || *src == '\0')
+ break;
+ *dst++ = *src++;
+ }
+
+ rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */
+
+ return(SendPacket(rconn)); /* send packet */
+}
+
+/*
+** SendFileNo -- send the name of a bootable file to the requester.
+**
+** Parameters:
+** req - RMP BOOT packet containing the request.
+** rconn - the reply packet to be formatted.
+** filelist - list of files available to the requester.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendFileNo(struct rmp_packet *req, RMPCONN *rconn, char *filelist[])
+{
+ struct rmp_packet *rpl;
+ char *src, *dst;
+ u_int8_t *size;
+ int i;
+
+ GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */
+ rpl = &rconn->rmp; /* cache ptr to RMP packet */
+
+ /*
+ * Set up assorted fields in reply packet.
+ */
+ rpl->r_brpl.rmp_type = RMP_BOOT_REPL;
+ PUTWORD(i, rpl->r_brpl.rmp_seqno);
+ i--;
+ rpl->r_brpl.rmp_session = 0;
+ rpl->r_brpl.rmp_version = htons(RMP_VERSION);
+
+ size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */
+ *size = 0; /* init length to zero */
+
+ /*
+ * Copy the file name into the reply packet incrementing the
+ * length as we go. Stop at end of string or when RMPBOOTDATA
+ * characters have been copied. Also, set return code to
+ * indicate success or "no more files".
+ */
+ if (i < C_MAXFILE && filelist[i] != NULL) {
+ src = filelist[i];
+ dst = (char *)&rpl->r_brpl.rmp_flnm;
+ for (; *src && *size < RMPBOOTDATA; (*size)++) {
+ if (*src == '\0')
+ break;
+ *dst++ = *src++;
+ }
+ rpl->r_brpl.rmp_retcode = RMP_E_OKAY;
+ } else
+ rpl->r_brpl.rmp_retcode = RMP_E_NODFLT;
+
+ rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */
+
+ return(SendPacket(rconn)); /* send packet */
+}
+
+/*
+** SendBootRepl -- open boot file and respond to boot request.
+**
+** Parameters:
+** req - RMP BOOT packet containing the request.
+** rconn - the reply packet to be formatted.
+** filelist - list of files available to the requester.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendBootRepl(struct rmp_packet *req, RMPCONN *rconn, char *filelist[])
+{
+ int retval;
+ char *filename, filepath[RMPBOOTDATA+1];
+ RMPCONN *oldconn;
+ struct rmp_packet *rpl;
+ char *src, *dst1, *dst2;
+ u_int8_t i;
+
+ /*
+ * If another connection already exists, delete it since we
+ * are obviously starting again.
+ */
+ if ((oldconn = FindConn(rconn)) != NULL) {
+ syslog(LOG_WARNING, "%s: dropping existing connection",
+ EnetStr(oldconn));
+ RemoveConn(oldconn);
+ }
+
+ rpl = &rconn->rmp; /* cache ptr to RMP packet */
+
+ /*
+ * Set up assorted fields in reply packet.
+ */
+ rpl->r_brpl.rmp_type = RMP_BOOT_REPL;
+ COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno);
+ rpl->r_brpl.rmp_session = htons(GenSessID());
+ rpl->r_brpl.rmp_version = htons(RMP_VERSION);
+ rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize;
+
+ /*
+ * Copy file name to `filepath' string, and into reply packet.
+ */
+ src = &req->r_brq.rmp_flnm;
+ dst1 = filepath;
+ dst2 = &rpl->r_brpl.rmp_flnm;
+ for (i = 0; i < req->r_brq.rmp_flnmsize; i++)
+ *dst1++ = *dst2++ = *src++;
+ *dst1 = '\0';
+
+ /*
+ * If we are booting HP-UX machines, their secondary loader will
+ * ask for files like "/hp-ux". As a security measure, we do not
+ * allow boot files to lay outside the boot directory (unless they
+ * are purposely link'd out. So, make `filename' become the path-
+ * stripped file name and spoof the client into thinking that it
+ * really got what it wanted.
+ */
+ filename = strrchr(filepath,'/');
+ filename = filename? filename + 1: filepath;
+
+ /*
+ * Check that this is a valid boot file name.
+ */
+ for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++)
+ if (STREQN(filename, filelist[i]))
+ goto match;
+
+ /*
+ * Invalid boot file name, set error and send reply packet.
+ */
+ rpl->r_brpl.rmp_retcode = RMP_E_NOFILE;
+ retval = 0;
+ goto sendpkt;
+
+match:
+ /*
+ * This is a valid boot file. Open the file and save the file
+ * descriptor associated with this connection and set success
+ * indication. If the file couldnt be opened, set error:
+ * "no such file or dir" - RMP_E_NOFILE
+ * "file table overflow" - RMP_E_BUSY
+ * "too many open files" - RMP_E_BUSY
+ * anything else - RMP_E_OPENFILE
+ */
+ if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) {
+ rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE:
+ (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY:
+ RMP_E_OPENFILE;
+ retval = 0;
+ } else {
+ rpl->r_brpl.rmp_retcode = RMP_E_OKAY;
+ retval = 1;
+ }
+
+sendpkt:
+ syslog(LOG_INFO, "%s: request to boot %s (%s)",
+ EnetStr(rconn), filename, retval? "granted": "denied");
+
+ rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize);
+
+ return (retval & SendPacket(rconn));
+}
+
+/*
+** SendReadRepl -- send a portion of the boot file to the requester.
+**
+** Parameters:
+** rconn - the reply packet to be formatted.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendReadRepl(RMPCONN *rconn)
+{
+ int retval = 0;
+ RMPCONN *oldconn;
+ struct rmp_packet *rpl, *req;
+ int size = 0;
+ int madeconn = 0;
+
+ /*
+ * Find the old connection. If one doesn't exist, create one only
+ * to return the error code.
+ */
+ if ((oldconn = FindConn(rconn)) == NULL) {
+ if ((oldconn = NewConn(rconn)) == NULL)
+ return(0);
+ syslog(LOG_ERR, "SendReadRepl: no active connection (%s)",
+ EnetStr(rconn));
+ madeconn++;
+ }
+
+ req = &rconn->rmp; /* cache ptr to request packet */
+ rpl = &oldconn->rmp; /* cache ptr to reply packet */
+
+ if (madeconn) { /* no active connection above; abort */
+ rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;
+ retval = 1;
+ goto sendpkt;
+ }
+
+ /*
+ * Make sure Session ID's match.
+ */
+ if (ntohs(req->r_rrq.rmp_session) !=
+ ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session):
+ ntohs(rpl->r_rrpl.rmp_session))) {
+ syslog(LOG_ERR, "SendReadRepl: bad session id (%s)",
+ EnetStr(rconn));
+ rpl->r_rrpl.rmp_retcode = RMP_E_BADSID;
+ retval = 1;
+ goto sendpkt;
+ }
+
+ /*
+ * If the requester asks for more data than we can fit,
+ * silently clamp the request size down to RMPREADDATA.
+ *
+ * N.B. I do not know if this is "legal", however it seems
+ * to work. This is necessary for bpfwrite() on machines
+ * with MCLBYTES less than 1514.
+ */
+ if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA)
+ req->r_rrq.rmp_size = htons(RMPREADDATA);
+
+ /*
+ * Position read head on file according to info in request packet.
+ */
+ GETWORD(req->r_rrq.rmp_offset, size);
+ if (lseek(oldconn->bootfd, (off_t)size, SEEK_SET) < 0) {
+ syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)",
+ EnetStr(rconn));
+ rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;
+ retval = 1;
+ goto sendpkt;
+ }
+
+ /*
+ * Read data directly into reply packet.
+ */
+ if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data,
+ (int) ntohs(req->r_rrq.rmp_size))) <= 0) {
+ if (size < 0) {
+ syslog(LOG_ERR, "SendReadRepl: read: %m (%s)",
+ EnetStr(rconn));
+ rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;
+ } else {
+ rpl->r_rrpl.rmp_retcode = RMP_E_EOF;
+ }
+ retval = 1;
+ goto sendpkt;
+ }
+
+ /*
+ * Set success indication.
+ */
+ rpl->r_rrpl.rmp_retcode = RMP_E_OKAY;
+
+sendpkt:
+ /*
+ * Set up assorted fields in reply packet.
+ */
+ rpl->r_rrpl.rmp_type = RMP_READ_REPL;
+ COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset);
+ rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session;
+
+ oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */
+
+ retval &= SendPacket(oldconn); /* send packet */
+
+ if (madeconn) /* clean up after ourself */
+ FreeConn(oldconn);
+
+ return (retval);
+}
+
+/*
+** BootDone -- free up memory allocated for a connection.
+**
+** Parameters:
+** rconn - incoming boot complete packet.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+BootDone(RMPCONN *rconn)
+{
+ RMPCONN *oldconn;
+ struct rmp_packet *rpl;
+
+ /*
+ * If we can't find the connection, ignore the request.
+ */
+ if ((oldconn = FindConn(rconn)) == NULL) {
+ syslog(LOG_ERR, "BootDone: no existing connection (%s)",
+ EnetStr(rconn));
+ return(0);
+ }
+
+ rpl = &oldconn->rmp; /* cache ptr to RMP packet */
+
+ /*
+ * Make sure Session ID's match.
+ */
+ if (ntohs(rconn->rmp.r_rrq.rmp_session) !=
+ ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session):
+ ntohs(rpl->r_rrpl.rmp_session))) {
+ syslog(LOG_ERR, "BootDone: bad session id (%s)",
+ EnetStr(rconn));
+ return(0);
+ }
+
+ RemoveConn(oldconn); /* remove connection */
+
+ syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn));
+
+ return(1);
+}
+
+/*
+** SendPacket -- send an RMP packet to a remote host.
+**
+** Parameters:
+** rconn - packet to be sent.
+**
+** Returns:
+** 1 on success, 0 on failure.
+**
+** Side Effects:
+** none.
+*/
+int
+SendPacket(RMPCONN *rconn)
+{
+ /*
+ * Set Ethernet Destination address to Source (BPF and the enet
+ * driver will take care of getting our source address set).
+ */
+ memmove((char *)&rconn->rmp.hp_hdr.daddr[0],
+ (char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN);
+ rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr));
+
+ /*
+ * Reverse 802.2/HP Extended Source & Destination Access Pts.
+ */
+ rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP);
+ rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP);
+
+ /*
+ * Last time this connection was active.
+ */
+ (void)gettimeofday(&rconn->tstamp, NULL);
+
+ if (DbgFp != NULL) /* display packet */
+ DispPkt(rconn,DIR_SENT);
+
+ /*
+ * Send RMP packet to remote host.
+ */
+ return(BpfWrite(rconn));
+}
diff --git a/libexec/rbootd/utils.c b/libexec/rbootd/utils.c
new file mode 100644
index 000000000000..e89c7d085f49
--- /dev/null
+++ b/libexec/rbootd/utils.c
@@ -0,0 +1,536 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1992 The University of Utah and the Center
+ * for Software Science (CSS).
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Center for Software Science of the University of Utah Computer
+ * Science Department. CSS requests users of this software to return
+ * to css-dist@cs.utah.edu any improvements that they make and grant
+ * CSS redistribution rights.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Utah Hdr: utils.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include "defs.h"
+
+/*
+** DispPkt -- Display the contents of an RMPCONN packet.
+**
+** Parameters:
+** rconn - packet to be displayed.
+** direct - direction packet is going (DIR_*).
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** None.
+*/
+void
+DispPkt(RMPCONN *rconn, int direct)
+{
+ static const char BootFmt[] = "\t\tRetCode:%u SeqNo:%x SessID:%x Vers:%u";
+ static const char ReadFmt[] = "\t\tRetCode:%u Offset:%x SessID:%x\n";
+
+ struct tm *tmp;
+ struct rmp_packet *rmp;
+ int i, omask;
+ u_int32_t t;
+
+ /*
+ * Since we will be working with RmpConns as well as DbgFp, we
+ * must block signals that can affect either.
+ */
+ omask = sigblock(sigmask(SIGHUP)|sigmask(SIGUSR1)|sigmask(SIGUSR2));
+
+ if (DbgFp == NULL) { /* sanity */
+ (void) sigsetmask(omask);
+ return;
+ }
+
+ /* display direction packet is going using '>>>' or '<<<' */
+ fputs((direct==DIR_RCVD)?"<<< ":(direct==DIR_SENT)?">>> ":"", DbgFp);
+
+ /* display packet timestamp */
+ tmp = localtime((time_t *)&rconn->tstamp.tv_sec);
+ fprintf(DbgFp, "%02d:%02d:%02d.%06ld ", tmp->tm_hour, tmp->tm_min,
+ tmp->tm_sec, rconn->tstamp.tv_usec);
+
+ /* display src or dst addr and information about network interface */
+ fprintf(DbgFp, "Addr: %s Intf: %s\n", EnetStr(rconn), IntfName);
+
+ rmp = &rconn->rmp;
+
+ /* display IEEE 802.2 Logical Link Control header */
+ (void) fprintf(DbgFp, "\t802.2 LLC: DSAP:%x SSAP:%x CTRL:%x\n",
+ rmp->hp_llc.dsap, rmp->hp_llc.ssap, ntohs(rmp->hp_llc.cntrl));
+
+ /* display HP extensions to 802.2 Logical Link Control header */
+ (void) fprintf(DbgFp, "\tHP Ext: DXSAP:%x SXSAP:%x\n",
+ ntohs(rmp->hp_llc.dxsap), ntohs(rmp->hp_llc.sxsap));
+
+ /*
+ * Display information about RMP packet using type field to
+ * determine what kind of packet this is.
+ */
+ switch(rmp->r_type) {
+ case RMP_BOOT_REQ: /* boot request */
+ (void) fprintf(DbgFp, "\tBoot Request:");
+ GETWORD(rmp->r_brq.rmp_seqno, t);
+ if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) {
+ if (WORDZE(rmp->r_brq.rmp_seqno))
+ fputs(" (Send Server ID)", DbgFp);
+ else
+ fprintf(DbgFp," (Send Filename #%u)",t);
+ }
+ (void) fputc('\n', DbgFp);
+ (void) fprintf(DbgFp, BootFmt, rmp->r_brq.rmp_retcode,
+ t, ntohs(rmp->r_brq.rmp_session),
+ ntohs(rmp->r_brq.rmp_version));
+ (void) fprintf(DbgFp, "\n\t\tMachine Type: ");
+ for (i = 0; i < RMP_MACHLEN; i++)
+ (void) fputc(rmp->r_brq.rmp_machtype[i], DbgFp);
+ DspFlnm(rmp->r_brq.rmp_flnmsize, &rmp->r_brq.rmp_flnm);
+ break;
+ case RMP_BOOT_REPL: /* boot reply */
+ fprintf(DbgFp, "\tBoot Reply:\n");
+ GETWORD(rmp->r_brpl.rmp_seqno, t);
+ (void) fprintf(DbgFp, BootFmt, rmp->r_brpl.rmp_retcode,
+ t, ntohs(rmp->r_brpl.rmp_session),
+ ntohs(rmp->r_brpl.rmp_version));
+ DspFlnm(rmp->r_brpl.rmp_flnmsize,&rmp->r_brpl.rmp_flnm);
+ break;
+ case RMP_READ_REQ: /* read request */
+ (void) fprintf(DbgFp, "\tRead Request:\n");
+ GETWORD(rmp->r_rrq.rmp_offset, t);
+ (void) fprintf(DbgFp, ReadFmt, rmp->r_rrq.rmp_retcode,
+ t, ntohs(rmp->r_rrq.rmp_session));
+ (void) fprintf(DbgFp, "\t\tNoOfBytes: %u\n",
+ ntohs(rmp->r_rrq.rmp_size));
+ break;
+ case RMP_READ_REPL: /* read reply */
+ (void) fprintf(DbgFp, "\tRead Reply:\n");
+ GETWORD(rmp->r_rrpl.rmp_offset, t);
+ (void) fprintf(DbgFp, ReadFmt, rmp->r_rrpl.rmp_retcode,
+ t, ntohs(rmp->r_rrpl.rmp_session));
+ (void) fprintf(DbgFp, "\t\tNoOfBytesSent: %zu\n",
+ rconn->rmplen - RMPREADSIZE(0));
+ break;
+ case RMP_BOOT_DONE: /* boot complete */
+ (void) fprintf(DbgFp, "\tBoot Complete:\n");
+ (void) fprintf(DbgFp, "\t\tRetCode:%u SessID:%x\n",
+ rmp->r_done.rmp_retcode,
+ ntohs(rmp->r_done.rmp_session));
+ break;
+ default: /* ??? */
+ (void) fprintf(DbgFp, "\tUnknown Type:(%d)\n",
+ rmp->r_type);
+ }
+ (void) fputc('\n', DbgFp);
+ (void) fflush(DbgFp);
+
+ (void) sigsetmask(omask); /* reset old signal mask */
+}
+
+
+/*
+** GetEtherAddr -- convert an RMP (Ethernet) address into a string.
+**
+** An RMP BOOT packet has been received. Look at the type field
+** and process Boot Requests, Read Requests, and Boot Complete
+** packets. Any other type will be dropped with a warning msg.
+**
+** Parameters:
+** addr - array of RMP_ADDRLEN bytes.
+**
+** Returns:
+** Pointer to static string representation of `addr'.
+**
+** Side Effects:
+** None.
+**
+** Warnings:
+** - The return value points to a static buffer; it must
+** be copied if it's to be saved.
+*/
+char *
+GetEtherAddr(u_int8_t *addr)
+{
+ static char Hex[] = "0123456789abcdef";
+ static char etherstr[RMP_ADDRLEN*3];
+ int i;
+ char *cp;
+
+ /*
+ * For each byte in `addr', convert it to "<hexchar><hexchar>:".
+ * The last byte does not get a trailing `:' appended.
+ */
+ i = 0;
+ cp = etherstr;
+ for(;;) {
+ *cp++ = Hex[*addr >> 4 & 0xf];
+ *cp++ = Hex[*addr++ & 0xf];
+ if (++i == RMP_ADDRLEN)
+ break;
+ *cp++ = ':';
+ }
+ *cp = '\0';
+
+ return(etherstr);
+}
+
+
+/*
+** DispFlnm -- Print a string of bytes to DbgFp (often, a file name).
+**
+** Parameters:
+** size - number of bytes to print.
+** flnm - address of first byte.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Characters are sent to `DbgFp'.
+*/
+void
+DspFlnm(u_int size, char *flnm)
+{
+ int i;
+
+ (void) fprintf(DbgFp, "\n\t\tFile Name (%u): <", size);
+ for (i = 0; i < size; i++)
+ (void) fputc(*flnm++, DbgFp);
+ (void) fputs(">\n", DbgFp);
+}
+
+
+/*
+** NewClient -- allocate memory for a new CLIENT.
+**
+** Parameters:
+** addr - RMP (Ethernet) address of new client.
+**
+** Returns:
+** Ptr to new CLIENT or NULL if we ran out of memory.
+**
+** Side Effects:
+** - Memory will be malloc'd for the new CLIENT.
+** - If malloc() fails, a log message will be generated.
+*/
+CLIENT *
+NewClient(u_int8_t *addr)
+{
+ CLIENT *ctmp;
+
+ if ((ctmp = (CLIENT *) malloc(sizeof(CLIENT))) == NULL) {
+ syslog(LOG_ERR, "NewClient: out of memory (%s)",
+ GetEtherAddr(addr));
+ return(NULL);
+ }
+
+ memset(ctmp, 0, sizeof(CLIENT));
+ memmove(&ctmp->addr[0], addr, RMP_ADDRLEN);
+ return(ctmp);
+}
+
+/*
+** FreeClient -- free linked list of Clients.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - All malloc'd memory associated with the linked list of
+** CLIENTS will be free'd; `Clients' will be set to NULL.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+FreeClients(void)
+{
+ CLIENT *ctmp;
+
+ while (Clients != NULL) {
+ ctmp = Clients;
+ Clients = Clients->next;
+ FreeClient(ctmp);
+ }
+}
+
+/*
+** NewStr -- allocate memory for a character array.
+**
+** Parameters:
+** str - null terminated character array.
+**
+** Returns:
+** Ptr to new character array or NULL if we ran out of memory.
+**
+** Side Effects:
+** - Memory will be malloc'd for the new character array.
+** - If malloc() fails, a log message will be generated.
+*/
+char *
+NewStr(char *str)
+{
+ char *stmp;
+
+ if ((stmp = (char *)malloc((unsigned) (strlen(str)+1))) == NULL) {
+ syslog(LOG_ERR, "NewStr: out of memory (%s)", str);
+ return(NULL);
+ }
+
+ (void) strcpy(stmp, str);
+ return(stmp);
+}
+
+/*
+** To save time, NewConn and FreeConn maintain a cache of one RMPCONN
+** in `LastFree' (defined below).
+*/
+
+static RMPCONN *LastFree = NULL;
+
+/*
+** NewConn -- allocate memory for a new RMPCONN connection.
+**
+** Parameters:
+** rconn - initialization template for new connection.
+**
+** Returns:
+** Ptr to new RMPCONN or NULL if we ran out of memory.
+**
+** Side Effects:
+** - Memory may be malloc'd for the new RMPCONN (if not cached).
+** - If malloc() fails, a log message will be generated.
+*/
+RMPCONN *
+NewConn(RMPCONN *rconn)
+{
+ RMPCONN *rtmp;
+
+ if (LastFree == NULL) { /* nothing cached; make a new one */
+ if ((rtmp = (RMPCONN *) malloc(sizeof(RMPCONN))) == NULL) {
+ syslog(LOG_ERR, "NewConn: out of memory (%s)",
+ EnetStr(rconn));
+ return(NULL);
+ }
+ } else { /* use the cached RMPCONN */
+ rtmp = LastFree;
+ LastFree = NULL;
+ }
+
+ /*
+ * Copy template into `rtmp', init file descriptor to `-1' and
+ * set ptr to next elem NULL.
+ */
+ memmove((char *)rtmp, (char *)rconn, sizeof(RMPCONN));
+ rtmp->bootfd = -1;
+ rtmp->next = NULL;
+
+ return(rtmp);
+}
+
+/*
+** FreeConn -- Free memory associated with an RMPCONN connection.
+**
+** Parameters:
+** rtmp - ptr to RMPCONN to be free'd.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Memory associated with `rtmp' may be free'd (or cached).
+** - File desc associated with `rtmp->bootfd' will be closed.
+*/
+void
+FreeConn(RMPCONN *rtmp)
+{
+ /*
+ * If the file descriptor is in use, close the file.
+ */
+ if (rtmp->bootfd >= 0) {
+ (void) close(rtmp->bootfd);
+ rtmp->bootfd = -1;
+ }
+
+ if (LastFree == NULL) /* cache for next time */
+ rtmp = LastFree;
+ else /* already one cached; free this one */
+ free((char *)rtmp);
+}
+
+/*
+** FreeConns -- free linked list of RMPCONN connections.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - All malloc'd memory associated with the linked list of
+** connections will be free'd; `RmpConns' will be set to NULL.
+** - If LastFree is != NULL, it too will be free'd & NULL'd.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+FreeConns(void)
+{
+ RMPCONN *rtmp;
+
+ while (RmpConns != NULL) {
+ rtmp = RmpConns;
+ RmpConns = RmpConns->next;
+ FreeConn(rtmp);
+ }
+
+ if (LastFree != NULL) {
+ free((char *)LastFree);
+ LastFree = NULL;
+ }
+}
+
+/*
+** AddConn -- Add a connection to the linked list of connections.
+**
+** Parameters:
+** rconn - connection to be added.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - RmpConn will point to new connection.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+AddConn(RMPCONN *rconn)
+{
+ if (RmpConns != NULL)
+ rconn->next = RmpConns;
+ RmpConns = rconn;
+}
+
+/*
+** FindConn -- Find a connection in the linked list of connections.
+**
+** We use the RMP (Ethernet) address as the basis for determining
+** if this is the same connection. According to the Remote Maint
+** Protocol, we can only have one connection with any machine.
+**
+** Parameters:
+** rconn - connection to be found.
+**
+** Returns:
+** Matching connection from linked list or NULL if not found.
+**
+** Side Effects:
+** None.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+RMPCONN *
+FindConn(RMPCONN *rconn)
+{
+ RMPCONN *rtmp;
+
+ for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next)
+ if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0],
+ (char *)&rtmp->rmp.hp_hdr.saddr[0], RMP_ADDRLEN) == 0)
+ break;
+
+ return(rtmp);
+}
+
+/*
+** RemoveConn -- Remove a connection from the linked list of connections.
+**
+** Parameters:
+** rconn - connection to be removed.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - If found, an RMPCONN will cease to exist and it will
+** be removed from the linked list.
+**
+** Warnings:
+** - This routine must be called with SIGHUP blocked.
+*/
+void
+RemoveConn(RMPCONN *rconn)
+{
+ RMPCONN *thisrconn, *lastrconn;
+
+ if (RmpConns == rconn) { /* easy case */
+ RmpConns = RmpConns->next;
+ FreeConn(rconn);
+ } else { /* must traverse linked list */
+ lastrconn = RmpConns; /* set back ptr */
+ thisrconn = lastrconn->next; /* set current ptr */
+ while (thisrconn != NULL) {
+ if (rconn == thisrconn) { /* found it */
+ lastrconn->next = thisrconn->next;
+ FreeConn(thisrconn);
+ break;
+ }
+ lastrconn = thisrconn;
+ thisrconn = thisrconn->next;
+ }
+ }
+}
diff --git a/libexec/rc/Makefile b/libexec/rc/Makefile
new file mode 100644
index 000000000000..e82b582462d0
--- /dev/null
+++ b/libexec/rc/Makefile
@@ -0,0 +1,31 @@
+.include <src.opts.mk>
+
+CONFGROUPS= CONFETC CONFETCEXEC CONFETCDEFAULTS
+CONFETCDIR= /etc
+CONFETC= network.subr rc rc.initdiskless rc.subr rc.shutdown rc.bsdextended
+CONFETCPACKAGE= rc
+
+.if ${MK_IPFW} != "no"
+CONFETC+= rc.firewall
+.endif
+CONFETCMODE= 644
+CONFETCEXEC= netstart pccard_ether rc.resume rc.suspend
+CONFETCEXECDIR= /etc
+CONFETCEXECMODE= 755
+CONFETCEXECPACKAGE= rc
+CONFETCDEFAULTSDIR= /etc/defaults
+CONFETCDEFAULTS= rc.conf
+CONFETCDEFAULTSPACKAGE= rc
+
+FILESGROUPS= LIBEXEC_SCRIPTS
+LIBEXEC_SCRIPTS= debug.sh hooks.sh safe_eval.sh
+LIBEXEC_SCRIPTSDIR= /libexec
+LIBEXEC_SCRIPTSMODE= 755
+LIBEXEC_SCRIPTSPACKAGE= rc
+
+SUBDIR+= rc.d
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
+.include <bsd.prog.mk>
diff --git a/libexec/rc/debug.sh b/libexec/rc/debug.sh
new file mode 100755
index 000000000000..739c81a709f6
--- /dev/null
+++ b/libexec/rc/debug.sh
@@ -0,0 +1,451 @@
+:
+# NAME:
+# debug.sh - selectively debug scripts
+#
+# SYNOPSIS:
+# $_DEBUG_SH . debug.sh
+# DebugOn [-eo] "tag" ...
+# DebugOff [-eo] [rc="rc"] "tag" ...
+# Debugging
+# DebugAdd "tag"
+# DebugEcho ...
+# DebugLog ...
+# DebugShell "tag" ...
+# DebugTrace ...
+# Debug "tag" ...
+#
+# $DEBUG_SKIP echo skipped when Debug "tag" is true.
+# $DEBUG_DO echo only done when Debug "tag" is true.
+#
+# DESCRIPTION:
+# debug.sh provides the following functions to facilitate
+# flexible run-time tracing of complicated shell scripts.
+#
+# DebugOn turns tracing on if any "tag" is found in "DEBUG_SH".
+# It turns tracing off if "!tag" is found in "DEBUG_SH".
+# It also sets "DEBUG_ON" to the "tag" that caused tracing to be
+# enabled, or "DEBUG_OFF" if we matched "!tag".
+# If '-e' option given returns 1 if no "tag" matched.
+# If the '-o' flag is given, tracing is turned off unless there
+# was a matched "tag", useful for functions too noisy to tace.
+#
+# Further; when we set "DEBUG_ON" if we find
+# "$DEBUG_ON:debug_add:tag" in "DEBUG_SH" we will
+# add the new "tag" to "DEBUG_SH" so it only has effect after that
+# point.
+#
+# DebugOff turns tracing on if any "tag" matches "DEBUG_OFF" or
+# off if any "tag" matches "DEBUG_ON". This allows nested
+# functions to not interfere with each other.
+#
+# DebugOff accepts but ignores the '-e' and '-o' options.
+# The optional "rc" value will be returned rather than the
+# default of 0. Thus if DebugOff is the last operation in a
+# function, "rc" will be the return code of that function.
+#
+# DebugAdd allows adding a "tag" to "DEBUG_SH" to influence
+# later events, possibly in a child process.
+#
+# DebugEcho is just shorthand for:
+#.nf
+# $DEBUG_DO echo "$@"
+#.fi
+#
+# Debugging returns true if tracing is enabled.
+# It is useful for bounding complex debug actions, rather than
+# using lots of "DEBUG_DO" lines.
+#
+# DebugShell runs an interactive shell if any "tag" is found in
+# "DEBUG_INTERACTIVE", and there is a tty available.
+# The shell used is defined by "DEBUG_SHELL" or "SHELL" and
+# defaults to '/bin/sh'.
+#
+# Debug calls DebugOn and if that does not turn tracing on, it
+# calls DebugOff to turn it off.
+#
+# The variables "DEBUG_SKIP" and "DEBUG_DO" are set so as to
+# enable/disable code that should be skipped/run when debugging
+# is turned on. "DEBUGGING" is the same as "DEBUG_SKIP" for
+# backwards compatability.
+#
+# The use of $_DEBUG_SH is to prevent multiple inclusion, though
+# it does no harm in this case.
+#
+# BUGS:
+# Does not work with some versions of ksh.
+# If a function turns tracing on, ksh turns it off when the
+# function returns - useless.
+# PD ksh works ok ;-)
+#
+# AUTHOR:
+# Simon J. Gerraty <sjg@crufty.net>
+
+# RCSid:
+# $Id: debug.sh,v 1.47 2025/08/07 21:59:54 sjg Exp $
+#
+# @(#) Copyright (c) 1994-2024 Simon J. Gerraty
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+_DEBUG_SH=:
+
+Myname=${Myname:-`basename $0 .sh`}
+
+DEBUGGING=
+DEBUG_DO=:
+DEBUG_SKIP=
+export DEBUGGING DEBUG_DO DEBUG_SKIP
+
+# have is handy
+if test -z "$_HAVE_SH"; then
+ _HAVE_SH=:
+
+ ##
+ # have that does not rely on return code of type
+ #
+ have() {
+ case `(type "$1") 2>&1` in
+ *" found") return 1;;
+ esac
+ return 0
+ }
+fi
+
+# does local *actually* work?
+local_works() {
+ local _fu
+}
+
+if local_works > /dev/null 2>&1; then
+ _local=local
+else
+ _local=:
+fi
+# for backwards compatability
+local=$_local
+
+if test -z "$isPOSIX_SHELL"; then
+ if (echo ${PATH%:*}) > /dev/null 2>&1; then
+ # true should be a builtin, : certainly is
+ isPOSIX_SHELL=:
+ else
+ isPOSIX_SHELL=false
+ false() {
+ return 1
+ }
+ fi
+fi
+
+is_posix_shell() {
+ $isPOSIX_SHELL
+ return
+}
+
+
+##
+# _debugAdd match
+#
+# Called from _debugOn when $match also appears in $DEBUG_SH with
+# a suffix of :debug_add:tag we will add tag to DEBUG_SH
+#
+_debugAdd() {
+ eval $_local tag
+
+ for tag in `IFS=,; echo $DEBUG_SH`
+ do
+ : tag=$tag
+ case "$tag" in
+ $1:debug_add:*)
+ if is_posix_shell; then
+ tag=${tag#$1:debug_add:}
+ else
+ tag=`expr $tag : '.*:debug_add:\(.*\)'`
+ fi
+ case ",$DEBUG_SH," in
+ *,$tag,*) ;;
+ *) set -x
+ : _debugAdd $1
+ DEBUG_SH=$DEBUG_SH,$tag
+ set +x
+ ;;
+ esac
+ ;;
+ esac
+ done
+ export DEBUG_SH
+}
+
+
+##
+# _debugOn match first
+#
+# Actually turn on tracing, set $DEBUG_ON=$match
+#
+# Check if $DEBUG_SH contains $match:debug_add:* and call _debugAdd
+# to add the suffix to DEBUG_SH. This useful when we only want
+# to trace some script when run under specific circumstances.
+#
+# If we have included hooks.sh $_HOOKS_SH will be set
+# and if $first (the first arg to DebugOn) is suitable as a variable
+# name we will run ${first}_debugOn_hooks.
+#
+# We disable tracing for hooks_run itself but functions can trace
+# if they want based on DEBUG_DO
+#
+_debugOn() {
+ DEBUG_OFF=
+ DEBUG_DO=
+ DEBUG_SKIP=:
+ DEBUG_X=-x
+ # do this firt to reduce noise
+ case ",$DEBUG_SH," in
+ *,$1:debug_add:*) _debugAdd $1;;
+ *,$2:debug_add:*) _debugAdd $2;;
+ esac
+ set -x
+ DEBUG_ON=$1
+ case "$_HOOKS_SH,$2" in
+ ,*|:,|:,*[${CASE_CLASS_NEG:-!}A-Za-z0-9_]*) ;;
+ *) # avoid noise from hooks_run
+ set +x
+ hooks_run ${2}_debugOn_hooks
+ set -x
+ ;;
+ esac
+}
+
+##
+# _debugOff match $DEBUG_ON $first
+#
+# Actually turn off tracing, set $DEBUG_OFF=$match
+#
+# If we have included hooks.sh $_HOOKS_SH will be set
+# and if $first (the first arg to DebugOff) is suitable as a variable
+# name we will run ${first}_debugOff_hooks.
+#
+# We do hooks_run after turning off tracing, but before resetting
+# DEBUG_DO so functions can trace if they want
+#
+_debugOff() {
+ DEBUG_OFF=$1
+ set +x
+ case "$_HOOKS_SH,$3" in
+ ,*|:,|:,*[${CASE_CLASS_NEG:-!}A-Za-z0-9_]*) ;;
+ *) hooks_run ${3}_debugOff_hooks;;
+ esac
+ set +x # just to be sure
+ DEBUG_ON=$2
+ DEBUG_DO=:
+ DEBUG_SKIP=
+ DEBUG_X=
+}
+
+##
+# DebugAdd tag
+#
+# Add tag to DEBUG_SH
+#
+DebugAdd() {
+ DEBUG_SH=${DEBUG_SH:+$DEBUG_SH,}$1
+ export DEBUG_SH
+}
+
+##
+# DebugEcho message
+#
+# Output message if we are debugging
+#
+DebugEcho() {
+ $DEBUG_DO echo "$@"
+}
+
+##
+# Debugging
+#
+# return 0 if we are debugging.
+#
+Debugging() {
+ test "$DEBUG_SKIP"
+}
+
+##
+# DebugLog message
+#
+# Outout message with timestamp if we are debugging
+#
+DebugLog() {
+ $DEBUG_SKIP return 0
+ echo `date '+@ %s [%Y-%m-%d %H:%M:%S %Z]'` "$@"
+}
+
+##
+# DebugTrace message
+#
+# Something hard to miss when wading through huge -x output
+#
+DebugTrace() {
+ $DEBUG_SKIP return 0
+ set +x
+ echo "@ ==================== [ $DEBUG_ON ] ===================="
+ DebugLog "$@"
+ echo "@ ==================== [ $DEBUG_ON ] ===================="
+ set -x
+}
+
+##
+# DebugOn [-e] [-o] match ...
+#
+# Turn on debugging if any $match is found in $DEBUG_SH.
+#
+DebugOn() {
+ eval ${local:-:} _e _match _off _rc
+ _rc=0 # avoid problems with set -e
+ _off=:
+ while :
+ do
+ case "$1" in
+ -e) _rc=1; shift;; # caller ok with return 1
+ -o) _off=; shift;; # off unless we have a match
+ *) break;;
+ esac
+ done
+ case ",${DEBUG_SH:-$DEBUG}," in
+ ,,) return $_rc;;
+ *,[Dd]ebug,*) ;;
+ *) $DEBUG_DO set +x;; # reduce the noise
+ esac
+ _match=
+ # if debugging is off because of a !e
+ # don't add 'all' to the On list.
+ case "$_off$DEBUG_OFF" in
+ :) _e=all;;
+ *) _e=;;
+ esac
+ for _e in ${*:-$Myname} $_e
+ do
+ : $_e in ,${DEBUG_SH:-$DEBUG},
+ case ",${DEBUG_SH:-$DEBUG}," in
+ *,!$_e,*|*,!$Myname:$_e,*)
+ # only turn it off if it was on
+ _rc=0
+ $DEBUG_DO _debugOff $_e $DEBUG_ON $1
+ break
+ ;;
+ *,$_e,*|*,$Myname:$_e,*)
+ # only turn it on if it was off
+ _rc=0
+ _match=$_e
+ $DEBUG_SKIP _debugOn $_e $1
+ break
+ ;;
+ esac
+ done
+ if test -z "$_off$_match"; then
+ # off unless explicit match, but
+ # only turn it off if it was on
+ $DEBUG_DO _debugOff $_e $DEBUG_ON $1
+ fi
+ DEBUGGING=$DEBUG_SKIP # backwards compatability
+ $DEBUG_DO set -x # back on if needed
+ $DEBUG_DO set -x # make sure we see it in trace
+ return $_rc
+}
+
+##
+# DebugOff [-e] [-o] [rc=$?] match ...
+#
+# Only turn debugging off if one of our args was the reason it
+# was turned on.
+#
+# We normally return 0, but caller can pass rc=$? as first arg
+# so that we preserve the status of last statement.
+#
+# The options '-e' and '-o' are ignored, they just make it easier to
+# keep DebugOn and DebugOff lines in sync.
+#
+DebugOff() {
+ eval ${local:-:} _e _rc
+ case ",${DEBUG_SH:-$DEBUG}," in
+ *,[Dd]ebug,*) ;;
+ *) $DEBUG_DO set +x;; # reduce the noise
+ esac
+ _rc=0 # always happy
+ while :
+ do
+ case "$1" in
+ -[eo]) shift;; # ignore it
+ rc=*) eval "_$1"; shift;;
+ *) break;;
+ esac
+ done
+ for _e in $*
+ do
+ : $_e==$DEBUG_OFF DEBUG_OFF
+ case "$DEBUG_OFF" in
+ "") break;;
+ $_e) _debugOn $DEBUG_ON $1; return $_rc;;
+ esac
+ done
+ for _e in $*
+ do
+ : $_e==$DEBUG_ON DEBUG_ON
+ case "$DEBUG_ON" in
+ "") break;;
+ $_e) _debugOff "" "" $1; return $_rc;;
+ esac
+ done
+ DEBUGGING=$DEBUG_SKIP # backwards compatability
+ $DEBUG_DO set -x # back on if needed
+ $DEBUG_DO set -x # make sure we see it in trace
+ return $_rc
+}
+
+_TTY=${_TTY:-`test -t 0 && tty`}; export _TTY
+
+# override this if you like
+_debugShell() {
+ test "x$_TTY" != x || return 0
+ {
+ echo DebugShell "$@"
+ echo "Type 'exit' to continue..."
+ } > $_TTY
+ ${DEBUG_SHELL:-${SHELL:-/bin/sh}} < $_TTY > $_TTY 2>&1
+}
+
+# Run an interactive shell if appropriate
+# Note: you can use $DEBUG_SKIP DebugShell ... to skip unless debugOn
+DebugShell() {
+ eval ${local:-:} _e
+ case "$_TTY%${DEBUG_INTERACTIVE}" in
+ *%|%*) return 0;; # no tty or no spec
+ esac
+ for _e in ${*:-$Myname} all
+ do
+ case ",${DEBUG_INTERACTIVE}," in
+ *,!$_e,*|*,!$Myname:$_e,*)
+ return 0
+ ;;
+ *,$_e,*|*,$Myname:$_e,*)
+ # Provide clues as to why/where
+ _debugShell "$_e: $@"
+ return $?
+ ;;
+ esac
+ done
+ return 0
+}
+
+# For backwards compatability
+Debug() {
+ case "${DEBUG_SH:-$DEBUG}" in
+ "") ;;
+ *) DEBUG_ON=${DEBUG_ON:-_Debug}
+ DebugOn -e $* || DebugOff $DEBUG_LAST
+ DEBUGGING=$DEBUG_SKIP
+ ;;
+ esac
+}
diff --git a/libexec/rc/hooks.sh b/libexec/rc/hooks.sh
new file mode 100755
index 000000000000..af4aff3d6bc5
--- /dev/null
+++ b/libexec/rc/hooks.sh
@@ -0,0 +1,274 @@
+:
+# NAME:
+# hooks.sh - provide hooks for customization
+#
+# SYNOPSIS:
+# hooks_add_all HOOKS [--first] func [...]
+# hooks_add_once HOOKS [--first] func [...]
+# hooks_add_default_set {all,once}
+# hooks_add HOOKS func [...]
+# hooks_get [--lifo] HOOKS
+# hooks_run [--lifo] HOOKS ["args"]
+# hooks_run_all [--lifo] HOOKS ["args"]
+# hooks_has HOOKS func
+#
+# add_hooks HOOKS [--first] func [...]
+# run_hooks HOOKS [LIFO] ["args"]
+# run_hooks_all HOOKS [LIFO] ["args"]
+#
+# DESCRIPTION:
+# The functions add_hooks and run_hooks are retained for
+# backwards compatibility. They are aliases for hooks_add and
+# hooks_run.
+#
+# hooks_add_all simply adds the "func"s to the list "HOOKS".
+#
+# If the first arg is '--first' "func"s are added to the start
+# of the list.
+#
+# hooks_add_once does the same but only if "func" is not in "HOOKS".
+# hooks_add uses one of the above based on "option", '--all' (default)
+# or '--once'.
+#
+# hooks_add_default_set sets the default behavior of hooks_add
+#
+# hooks_get simply returns the named list of functions.
+#
+# hooks_has indicates whether "func" in in "HOOKS".
+#
+# hooks_run runs each "func" in $HOOKS and stops if any of them
+# return a bad status.
+#
+# hooks_run_all does the same but does not stop on error.
+#
+# If run_hooks or run_hooks_all is given a flag of '--lifo' or
+# 2nd argument of LIFO the hooks are run in the reverse order of
+# calls to hooks_add.
+# Any "args" specified are passed to each hook function.
+#
+
+# RCSid:
+# $Id: hooks.sh,v 1.26 2025/08/07 21:59:54 sjg Exp $
+#
+# @(#)Copyright (c) 2000-2024 Simon J. Gerraty
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+# avoid multiple inclusion
+_HOOKS_SH=:
+
+# does local *actually* work?
+local_works() {
+ local _fu
+}
+
+if local_works > /dev/null 2>&1; then
+ _local=local
+else
+ _local=:
+fi
+# for backwards compatability
+local=$_local
+
+
+##
+# hooks_add_all list func ...
+#
+# add "func"s to "list" regardless
+#
+hooks_add_all() {
+ eval $_local __h
+ __h=$1; shift
+ case "$1" in
+ --first)
+ shift
+ eval "$__h=\"$* \$$__h\""
+ ;;
+ *) eval "$__h=\"\$$__h $*\"";;
+ esac
+}
+
+##
+# hooks_add_once list func ...
+#
+# add "func"s to "list" if not already there
+#
+hooks_add_once() {
+ eval $_local __h __hh __first
+ __h=$1; shift
+ case "$1" in
+ --first) shift; __first=:;;
+ *) __first=;;
+ esac
+ eval "__hh=\$$__h"
+ while [ $# -gt 0 ]
+ do
+ : __hh="$__hh" 1="$1"
+ case "$__first $__hh " in
+ *" $1 "*) ;; # dupe
+ :*) __hh="$1 $__hh";;
+ *) __hh="$__hh $1";;
+ esac
+ shift
+ done
+ eval "$__h=\"$__hh\""
+}
+
+##
+# hooks_add_default_set [--]{all,once}
+#
+# change the default method of hooks_add
+#
+hooks_add_default_set() {
+ case "$1" in
+ once|--once) HOOKS_ADD_DEFAULT=once;;
+ *) HOOKS_ADD_DEFAULT=all;;
+ esac
+}
+
+##
+# hooks_add [--{all,once}] list func ...
+#
+# add "func"s to "list"
+#
+# If '--once' use hooks_add_once,
+# default is hooks_add_all.
+#
+hooks_add() {
+ case "$1" in
+ --all) shift; hooks_add_all "$@";;
+ --once) shift; hooks_add_once "$@";;
+ *) hooks_add_${HOOKS_ADD_DEFAULT:-all} "$@";;
+ esac
+}
+
+##
+# hooks_get [--lifo] list [LIFO]
+#
+# return $list
+#
+hooks_get() {
+ eval $_local __h __h2 e __l
+ case "$1" in
+ --lifo) __l=LIFO; shift;;
+ esac
+ eval "__h=\$$1"
+ case "$__l$2" in
+ LIFO*)
+ __h2="$__h"
+ __h=
+ for e in $__h2
+ do
+ __h="$e $__h"
+ done
+ ;;
+ esac
+ echo "$__h"
+}
+
+##
+# hooks_has list func
+#
+# is func in $list ?
+#
+hooks_has() {
+ eval $_local __h
+ eval "__h=\$$1"
+ case " $__h " in
+ *" $1 "*) return 0;;
+ esac
+ return 1
+}
+
+##
+# hooks_run [--all] [--lifo] list [LIFO] [args]
+#
+# pass "args" to each function in "list"
+# Without '--all'; if any return non-zero return that immediately
+#
+hooks_run() {
+ eval $_local __a e __h __hl __h2 __l
+ __a=return
+ __l=
+
+ while :
+ do
+ case "$1" in
+ --all) __a=:; shift;;
+ --lifo) __l=$1; shift;;
+ *) break;;
+ esac
+ done
+ __hl=$1; shift
+ case "$1" in
+ LIFO) __l=--lifo; shift;;
+ esac
+ __h=`hooks_get $__l $__hl`
+ for e in $__h
+ do
+ $e "$@" || $__a $?
+ done
+}
+
+##
+# hooks_run_all [--lifo] list [LIFO] [args]
+#
+# pass "args" to each function in "list"
+#
+hooks_run_all() {
+ hooks_run --all "$@"
+}
+
+##
+# add_hooks,run_hooks[_all] aliases
+#
+add_hooks() {
+ hooks_add "$@"
+}
+
+run_hooks() {
+ hooks_run "$@"
+}
+
+run_hooks_all() {
+ hooks_run --all "$@"
+}
+
+
+case /$0 in
+*/hooks.sh)
+ # simple unit-test
+ list=HOOKS
+ flags=
+ while :
+ do
+ : 1=$1
+ case "$1" in
+ HOOKS|*hooks) list=$1; shift;;
+ --*) flags="$flags $1"; shift;;
+ *) break;;
+ esac
+ done
+ for f in "$@"
+ do
+ : f=$f
+ case "$f" in
+ LIFO) ;;
+ false|true) ;;
+ *) eval "$f() { echo This is $f; }";;
+ esac
+ done
+ echo hooks_add $flags $list "$@"
+ hooks_add $flags $list "$@"
+ echo hooks_run $list
+ hooks_run $list
+ echo hooks_run --all --lifo $list
+ hooks_run --all --lifo $list
+ echo hooks_run $list LIFO
+ hooks_run $list LIFO
+ ;;
+esac
diff --git a/libexec/rc/netstart b/libexec/rc/netstart
new file mode 100755
index 000000000000..430a578a73ea
--- /dev/null
+++ b/libexec/rc/netstart
@@ -0,0 +1,52 @@
+#!/bin/sh -
+#
+# Copyright (c) 1993 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# This file is NOT called by any of the other scripts - it has been
+# obsoleted by /etc/rc.d/* and is provided here only for user
+# convenience (if you're sitting in single user mode and wish to start
+# the network by hand, this script will do it for you).
+#
+
+_start=quietstart
+
+/etc/rc.d/devd ${_start}
+/etc/rc.d/hostid ${_start}
+/etc/rc.d/hostname ${_start}
+/etc/rc.d/ipmon ${_start}
+/etc/rc.d/ipfilter ${_start}
+/etc/rc.d/ipnat ${_start}
+/etc/rc.d/ipfs ${_start}
+/etc/rc.d/netif ${_start}
+/etc/rc.d/ipsec ${_start}
+/etc/rc.d/ppp ${_start}
+/etc/rc.d/ipfw ${_start}
+/etc/rc.d/routing ${_start}
+/etc/rc.d/route6d ${_start}
+/etc/rc.d/routed ${_start}
+/etc/rc.d/rtsold ${_start}
+/etc/rc.d/nisdomain ${_start}
+
+exit 0
diff --git a/libexec/rc/network.subr b/libexec/rc/network.subr
new file mode 100644
index 000000000000..5e4f2c1f39a0
--- /dev/null
+++ b/libexec/rc/network.subr
@@ -0,0 +1,1790 @@
+#
+# Copyright (c) 2003 The FreeBSD Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+IFCONFIG_CMD="/sbin/ifconfig"
+: ${netif_ipexpand_max:=2048}
+
+#
+# Subroutines commonly used from network startup scripts.
+# Requires that rc.conf be loaded first.
+#
+
+# ifn_start ifn
+# Bring up and configure an interface. If some configuration is
+# applied, print the interface configuration.
+#
+ifn_start()
+{
+ local ifn cfg
+ ifn="$1"
+ cfg=1
+
+ [ -z "$ifn" ] && err 1 "ifn_start called without an interface"
+
+ ifscript_up ${ifn} && cfg=0
+ ifconfig_up ${ifn} && cfg=0
+ if ! noafif $ifn; then
+ afexists inet6 && ipv6_up ${ifn} && cfg=0
+ afexists inet && ipv4_up ${ifn} && cfg=0
+ fi
+ childif_create ${ifn} && cfg=0
+
+ return $cfg
+}
+
+# ifn_stop ifn
+# Shutdown and de-configure an interface. If action is taken,
+# print the interface name.
+#
+ifn_stop()
+{
+ local ifn cfg
+ ifn="$1"
+ cfg=1
+
+ [ -z "$ifn" ] && err 1 "ifn_stop called without an interface"
+
+ if ! noafif $ifn; then
+ afexists inet && ipv4_down ${ifn} && cfg=0
+ afexists inet6 && ipv6_down ${ifn} && cfg=0
+ fi
+ ifconfig_down ${ifn} && cfg=0
+ ifscript_down ${ifn} && cfg=0
+ childif_destroy ${ifn} && cfg=0
+
+ return $cfg
+}
+
+# ifn_vnetup ifn
+# Move ifn to the specified vnet jail.
+#
+ifn_vnetup()
+{
+
+ ifn_vnet0 $1 vnet
+}
+
+# ifn_vnetdown ifn
+# Reclaim ifn from the specified vnet jail.
+#
+ifn_vnetdown()
+{
+
+ ifn_vnet0 $1 -vnet
+}
+
+# ifn_vnet0 ifn action
+# Helper function for ifn_vnetup and ifn_vnetdown.
+#
+ifn_vnet0()
+{
+ local _ifn _cfg _action _vnet
+ _ifn="$1"
+ _action="$2"
+ _cfg=1
+
+ if _vnet=$(vnetif $_ifn); then
+ ${IFCONFIG_CMD} $_ifn $_action $_vnet && _cfg=0
+ fi
+
+ return $_cfg
+}
+
+# ifconfig_up if
+# Evaluate ifconfig(8) arguments for interface $if and
+# run ifconfig(8) with those arguments. It returns 0 if
+# arguments were found and executed or 1 if the interface
+# had no arguments. Pseudo arguments DHCP and WPA are handled
+# here.
+#
+ifconfig_up()
+{
+ local _cfg _ifconfig_descr _ipv6_opts ifconfig_args
+ _cfg=1
+
+ # Make sure lo0 always comes up.
+ if [ "$1" = "lo0" ]; then
+ _cfg=0
+ fi
+
+ # inet6 specific
+ if ! noafif $1 && afexists inet6; then
+ if checkyesno ipv6_activate_all_interfaces; then
+ _ipv6_opts="-ifdisabled"
+ fi
+
+ # backward compatibility: $ipv6_enable
+ case $ipv6_enable in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+ case $1 in
+ bridge[0-9]*)
+ # No accept_rtadv by default on if_bridge(4)
+ # to avoid a conflict with the member
+ # interfaces.
+ ;;
+ *)
+ if ! checkyesno ipv6_gateway_enable; then
+ _ipv6_opts="${_ipv6_opts} accept_rtadv"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+
+ case $ipv6_cpe_wanif in
+ $1)
+ _ipv6_opts="${_ipv6_opts} -no_radr accept_rtadv"
+ ;;
+ esac
+
+ if [ -n "${_ipv6_opts}" ]; then
+ ${IFCONFIG_CMD} $1 inet6 ${_ipv6_opts}
+ fi
+ fi
+
+ # ifconfig_IF
+ ifconfig_args=`ifconfig_getargs $1`
+ if [ -n "${ifconfig_args}" ]; then
+ eval ${IFCONFIG_CMD} $1 ${ifconfig_args}
+ _cfg=0
+ fi
+
+ # inet6 specific
+ if ! noafif $1 && afexists inet6; then
+ # ifconfig_IF_ipv6
+ ifconfig_args=`ifconfig_getargs $1 ipv6`
+ if [ -n "${ifconfig_args}" ]; then
+ # backward compatibility: inet6 keyword
+ case "${ifconfig_args}" in
+ :*|[0-9a-fA-F]*:*)
+ warn "\$ifconfig_$1_ipv6 needs leading" \
+ "\"inet6\" keyword for an IPv6 address."
+ ifconfig_args="inet6 ${ifconfig_args}"
+ ;;
+ esac
+ ${IFCONFIG_CMD} $1 inet6 -ifdisabled
+ eval ${IFCONFIG_CMD} $1 ${ifconfig_args}
+ _cfg=0
+ fi
+
+ # $ipv6_prefix_IF will be handled in
+ # ipv6_prefix_hostid_addr_common().
+ ifconfig_args=`get_if_var $1 ipv6_prefix_IF`
+ if [ -n "${ifconfig_args}" ]; then
+ ${IFCONFIG_CMD} $1 inet6 -ifdisabled
+ _cfg=0
+ fi
+
+ # backward compatibility: $ipv6_ifconfig_IF
+ ifconfig_args=`get_if_var $1 ipv6_ifconfig_IF`
+ if [ -n "${ifconfig_args}" ]; then
+ warn "\$ipv6_ifconfig_$1 is obsolete." \
+ " Use ifconfig_$1_ipv6 instead."
+ ${IFCONFIG_CMD} $1 inet6 -ifdisabled
+ eval ${IFCONFIG_CMD} $1 inet6 ${ifconfig_args}
+ _cfg=0
+ fi
+ fi
+
+ ifalias $1 link alias
+ ifalias $1 ether alias
+
+ _ifconfig_descr=`get_if_var $1 ifconfig_IF_descr`
+ if [ -n "${_ifconfig_descr}" ]; then
+ ${IFCONFIG_CMD} $1 description "${_ifconfig_descr}"
+ fi
+
+ if wpaif $1; then
+ /etc/rc.d/wpa_supplicant start $1
+ _cfg=0 # XXX: not sure this should count
+ elif hostapif $1; then
+ /etc/rc.d/hostapd start $1
+ _cfg=0
+ elif [ ${_cfg} -eq 0 ]; then
+ ${IFCONFIG_CMD} $1 up
+ fi
+
+ if ! noafif $1 && afexists inet6; then
+ ipv6_accept_rtadv_up $1 && _cfg=0
+ fi
+
+ if dhcpif $1; then
+ if [ $_cfg -ne 0 ] ; then
+ ${IFCONFIG_CMD} $1 up
+ fi
+ if syncdhcpif $1; then
+ /etc/rc.d/dhclient start $1
+ fi
+ _cfg=0
+ fi
+
+ return $_cfg
+}
+
+# ifconfig_down if
+# returns 1 if wpa_supplicant or dhclient was stopped or
+# the interface exists.
+#
+ifconfig_down()
+{
+ local _cfg
+ _cfg=1
+
+ if wpaif $1; then
+ /etc/rc.d/wpa_supplicant stop $1
+ _cfg=0
+ elif hostapif $1; then
+ /etc/rc.d/hostapd stop $1
+ _cfg=0
+ elif dhcpif $1; then
+ /etc/rc.d/dhclient stop $1
+ _cfg=0
+ fi
+
+ if ifexists $1; then
+ ${IFCONFIG_CMD} $1 down
+ _cfg=0
+ fi
+
+ return $_cfg
+}
+
+# get_if_var if var [default]
+# Return the value of the pseudo-hash corresponding to $if where
+# $var is a string containg the sub-string "IF" which will be
+# replaced with $if after the characters defined in _punct are
+# replaced with '_'. If the variable is unset, replace it with
+# $default if given.
+get_if_var()
+{
+ local _if _punct _punct_c _var _default prefix suffix
+
+ if [ $# -ne 2 -a $# -ne 3 ]; then
+ err 3 'USAGE: get_if_var name var [default]'
+ fi
+
+ _if=$1
+ _punct=".-/+"
+ ltr ${_if} "${_punct}" '_' _if
+ _var=$2
+ _default=$3
+
+ prefix=${_var%%IF*}
+ suffix=${_var##*IF}
+ eval echo \${${prefix}${_if}${suffix}-${_default}}
+}
+
+# _ifconfig_getargs if [af]
+# Prints the arguments for the supplied interface to stdout.
+# Returns 1 if empty. In general, ifconfig_getargs should be used
+# outside this file.
+_ifconfig_getargs()
+{
+ local _ifn _af
+ _ifn=$1
+ _af=${2+_$2}
+
+ if [ -z "$_ifn" ]; then
+ return 1
+ fi
+
+ get_if_var $_ifn ifconfig_IF$_af "$ifconfig_DEFAULT"
+}
+
+# ifconfig_getargs if [af]
+# Takes the result from _ifconfig_getargs and removes pseudo
+# args such as DHCP and WPA.
+ifconfig_getargs()
+{
+ local _tmpargs _arg _args _vnet
+ _tmpargs=`_ifconfig_getargs $1 $2`
+ if [ $? -eq 1 ]; then
+ return 1
+ fi
+ _args=
+ _vnet=0
+
+ for _arg in $_tmpargs; do
+ case $_arg:$_vnet in
+ [Dd][Hh][Cc][Pp]:0) ;;
+ [Nn][Oo][Aa][Uu][Tt][Oo]:0) ;;
+ [Nn][Oo][Ss][Yy][Nn][Cc][Dd][Hh][Cc][Pp]:0) ;;
+ [Ss][Yy][Nn][Cc][Dd][Hh][Cc][Pp]:0) ;;
+ [Ww][Pp][Aa]:0) ;;
+ [Hh][Oo][Ss][Tt][Aa][Pp]:0) ;;
+ vnet:0) _vnet=1 ;;
+ *:1) _vnet=0 ;;
+ *:0)
+ _args="$_args $_arg"
+ ;;
+ esac
+ done
+
+ echo $_args
+}
+
+# autoif
+# Returns 0 if the interface should be automatically configured at
+# boot time and 1 otherwise.
+autoif()
+{
+ local _tmpargs _arg
+ _tmpargs=`_ifconfig_getargs $1`
+
+ for _arg in $_tmpargs; do
+ case $_arg in
+ [Nn][Oo][Aa][Uu][Tt][Oo])
+ return 1
+ ;;
+ esac
+ done
+
+ return 0
+}
+
+# dhcpif if
+# Returns 0 if the interface is a DHCP interface and 1 otherwise.
+dhcpif()
+{
+ local _tmpargs _arg
+ _tmpargs=`_ifconfig_getargs $1`
+
+ case $1 in
+ lo[0-9]*|\
+ stf[0-9]*|\
+ lp[0-9]*|\
+ sl[0-9]*)
+ return 1
+ ;;
+ esac
+ if noafif $1; then
+ return 1
+ fi
+
+ for _arg in $_tmpargs; do
+ case $_arg in
+ [Dd][Hh][Cc][Pp])
+ return 0
+ ;;
+ [Nn][Oo][Ss][Yy][Nn][Cc][Dd][Hh][Cc][Pp])
+ return 0
+ ;;
+ [Ss][Yy][Nn][Cc][Dd][Hh][Cc][Pp])
+ return 0
+ ;;
+ esac
+ done
+
+ return 1
+}
+
+# syncdhcpif
+# Returns 0 if the interface should be configured synchronously and
+# 1 otherwise.
+syncdhcpif()
+{
+ local _tmpargs _arg
+ _tmpargs=`_ifconfig_getargs $1`
+
+ if noafif $1; then
+ return 1
+ fi
+
+ for _arg in $_tmpargs; do
+ case $_arg in
+ [Nn][Oo][Ss][Yy][Nn][Cc][Dd][Hh][Cc][Pp])
+ return 1
+ ;;
+ [Ss][Yy][Nn][Cc][Dd][Hh][Cc][Pp])
+ return 0
+ ;;
+ esac
+ done
+
+ checkyesno synchronous_dhclient
+}
+
+# wpaif if
+# Returns 0 if the interface is a WPA interface and 1 otherwise.
+wpaif()
+{
+ local _tmpargs _arg
+ _tmpargs=`_ifconfig_getargs $1`
+
+ for _arg in $_tmpargs; do
+ case $_arg in
+ [Ww][Pp][Aa])
+ return 0
+ ;;
+ esac
+ done
+
+ return 1
+}
+
+# hostapif if
+# Returns 0 if the interface is a HOSTAP interface and 1 otherwise.
+hostapif()
+{
+ local _tmpargs _arg
+ _tmpargs=`_ifconfig_getargs $1`
+
+ for _arg in $_tmpargs; do
+ case $_arg in
+ [Hh][Oo][Ss][Tt][Aa][Pp])
+ return 0
+ ;;
+ esac
+ done
+
+ return 1
+}
+
+# vnetif if
+# Returns 0 and echo jail if "vnet" keyword is specified on the
+# interface, and 1 otherwise.
+vnetif()
+{
+ local _tmpargs _arg _vnet
+ _tmpargs=`_ifconfig_getargs $1`
+
+ _vnet=0
+ for _arg in $_tmpargs; do
+ case $_arg:$_vnet in
+ vnet:0) _vnet=1 ;;
+ *:1) echo $_arg; return 0 ;;
+ esac
+ done
+
+ return 1
+}
+
+# afexists af
+# Returns 0 if the address family is enabled in the kernel
+# 1 otherwise.
+afexists()
+{
+ local _af
+ _af=$1
+
+ case ${_af} in
+ inet|inet6)
+ check_kern_features ${_af}
+ ;;
+ link|ether)
+ return 0
+ ;;
+ *)
+ err 1 "afexists(): Unsupported address family: $_af"
+ ;;
+ esac
+}
+
+# noafif if
+# Returns 0 if the interface has no af configuration and 1 otherwise.
+noafif()
+{
+ local _if
+ _if=$1
+
+ case $_if in
+ pflog[0-9]*|\
+ pfsync[0-9]*|\
+ usbus[0-9]*|\
+ an[0-9]*|\
+ ath[0-9]*|\
+ ipw[0-9]*|\
+ ipfw[0-9]*|\
+ iwi[0-9]*|\
+ iwn[0-9]*|\
+ ral[0-9]*|\
+ wi[0-9]*|\
+ wl[0-9]*|\
+ wpi[0-9]*)
+ return 0
+ ;;
+ esac
+
+ return 1
+}
+
+# ipv6if if
+# Returns 0 if the interface should be configured for IPv6 and
+# 1 otherwise.
+ipv6if()
+{
+ local _if _tmpargs i
+ _if=$1
+
+ if ! afexists inet6; then
+ return 1
+ fi
+
+ # lo0 is always IPv6-enabled
+ case $_if in
+ lo0)
+ return 0
+ ;;
+ esac
+
+ case "${ipv6_network_interfaces}" in
+ $_if|"$_if "*|*" $_if"|*" $_if "*|[Aa][Uu][Tt][Oo])
+ # True if $ifconfig_IF_ipv6 is defined.
+ _tmpargs=`_ifconfig_getargs $_if ipv6`
+ if [ -n "${_tmpargs}" ]; then
+ return 0
+ fi
+
+ # True if $ipv6_prefix_IF is defined.
+ _tmpargs=`get_if_var $_if ipv6_prefix_IF`
+ if [ -n "${_tmpargs}" ]; then
+ return 0
+ fi
+
+ # backward compatibility: True if $ipv6_ifconfig_IF is defined.
+ _tmpargs=`get_if_var $_if ipv6_ifconfig_IF`
+ if [ -n "${_tmpargs}" ]; then
+ return 0
+ fi
+ ;;
+ esac
+
+ return 1
+}
+
+# ipv6_autoconfif if
+# Returns 0 if the interface should be configured for IPv6 with
+# Stateless Address Configuration; 1 otherwise.
+ipv6_autoconfif()
+{
+ local _if _tmpargs _arg
+ _if=$1
+
+ case $_if in
+ lo[0-9]*|\
+ stf[0-9]*|\
+ lp[0-9]*|\
+ sl[0-9]*)
+ return 1
+ ;;
+ esac
+ if noafif $_if; then
+ return 1
+ fi
+ if ! ipv6if $_if; then
+ return 1
+ fi
+ if checkyesno ipv6_gateway_enable; then
+ return 1
+ fi
+ _tmpargs=`get_if_var $_if ipv6_prefix_IF`
+ if [ -n "${_tmpargs}" ]; then
+ return 1
+ fi
+ # backward compatibility: $ipv6_enable
+ case $ipv6_enable in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+ if checkyesno ipv6_gateway_enable; then
+ return 1
+ fi
+ case $1 in
+ bridge[0-9]*)
+ # No accept_rtadv by default on if_bridge(4)
+ # to avoid a conflict with the member
+ # interfaces.
+ return 1
+ ;;
+ *)
+ return 0
+ ;;
+ esac
+ ;;
+ esac
+
+ _tmpargs=`_ifconfig_getargs $_if ipv6`
+ for _arg in $_tmpargs; do
+ case $_arg in
+ accept_rtadv)
+ return 0
+ ;;
+ esac
+ done
+
+ # backward compatibility: $ipv6_ifconfig_IF
+ _tmpargs=`get_if_var $_if ipv6_ifconfig_IF`
+ for _arg in $_tmpargs; do
+ case $_arg in
+ accept_rtadv)
+ return 0
+ ;;
+ esac
+ done
+
+ return 1
+}
+
+# ifexists if
+# Returns 0 if the interface exists and 1 otherwise.
+ifexists()
+{
+ [ -z "$1" ] && return 1
+ ${IFCONFIG_CMD} -n $1 > /dev/null 2>&1
+}
+
+# ifisup if
+# Returns 0 if the interface exists and UP,
+# returns 1 if the interface exists and not UP,
+# returns 2 otherwise.
+ifisup()
+{
+ local _if
+
+ [ -z "$1" ] && return 2
+ _if="$1"
+
+ set -- $(${IFCONFIG_CMD} -n ${_if} 2>/dev/null)
+ case "$1$2" in
+ ${_if}:*'<UP'[,\>]*) return 0 ;;
+ ${_if}:*) return 1 ;;
+ esac
+
+ return 2
+}
+
+# ipv4_up if
+# add IPv4 addresses to the interface $if
+ipv4_up()
+{
+ local _if _ret
+ _if=$1
+ _ret=1
+
+ # Add 127.0.0.1/8 to lo0 unless otherwise specified.
+ if [ "${_if}" = "lo0" ]; then
+ ifconfig_args=`get_if_var ${_if} ifconfig_IF`
+ if [ -z "${ifconfig_args}" ]; then
+ ${IFCONFIG_CMD} ${_if} inet 127.0.0.1/8 alias
+ fi
+ fi
+ ifalias ${_if} inet alias && _ret=0
+
+ return $_ret
+}
+
+# ipv6_up if
+# add IPv6 addresses to the interface $if
+ipv6_up()
+{
+ local _if _ret
+ _if=$1
+ _ret=1
+
+ if ! ipv6if $_if; then
+ return 0
+ fi
+
+ ifalias ${_if} inet6 alias && _ret=0
+ ipv6_prefix_hostid_addr_common ${_if} alias && _ret=0
+
+ return $_ret
+}
+
+# ipv4_down if
+# remove IPv4 addresses from the interface $if
+ipv4_down()
+{
+ local _if _ifs _ret inetList oldifs _inet
+ _if=$1
+ _ifs="^"
+ _ret=1
+
+ ifalias ${_if} inet -alias && _ret=0
+
+ inetList="`${IFCONFIG_CMD} ${_if} | grep 'inet ' | tr "\n\t" "$_ifs"`"
+
+ oldifs="$IFS"
+ IFS="$_ifs"
+ for _inet in $inetList ; do
+ # get rid of extraneous line
+ case $_inet in
+ inet[[:space:]]*) ;;
+ *) continue ;;
+ esac
+
+ _inet=`expr "$_inet" : '.*\(inet \([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}\).*'`
+
+ IFS="$oldifs"
+ ${IFCONFIG_CMD} ${_if} ${_inet} delete
+ IFS="$_ifs"
+ _ret=0
+ done
+ IFS="$oldifs"
+
+ return $_ret
+}
+
+# ipv6_down if
+# remove IPv6 addresses from the interface $if
+ipv6_down()
+{
+ local _if _ifs _ret inetList oldifs _inet6
+ _if=$1
+ _ifs="^"
+ _ret=1
+
+ if ! ipv6if $_if; then
+ return 0
+ fi
+
+ ipv6_accept_rtadv_down ${_if} && _ret=0
+ ipv6_prefix_hostid_addr_common ${_if} -alias && _ret=0
+ ifalias ${_if} inet6 -alias && _ret=0
+
+ inetList="`${IFCONFIG_CMD} ${_if} | grep 'inet6 ' | tr "\n\t" "$_ifs"`"
+
+ oldifs="$IFS"
+ IFS="$_ifs"
+ for _inet6 in $inetList ; do
+ # get rid of extraneous line
+ case $_inet6 in
+ inet6[[:space:]]*) ;;
+ *) continue ;;
+ esac
+
+ _inet6=`expr "$_inet6" : '.*\(inet6 \([0-9a-f:]*\)\).*'`
+
+ IFS="$oldifs"
+ ${IFCONFIG_CMD} ${_if} ${_inet6} -alias
+ IFS="$_ifs"
+ _ret=0
+ done
+ IFS="$oldifs"
+
+ return $_ret
+}
+
+# ifalias if af action
+# Configure or remove aliases for network interface $if.
+# It returns 0 if at least one alias was configured or
+# removed, or 1 if there were none.
+#
+ifalias()
+{
+ local _ret
+ _ret=1
+
+ afexists $2 || return $_ret
+
+ case "$2" in
+ inet|inet6|link|ether)
+ ifalias_af_common $1 $2 $3 && _ret=0
+ ;;
+ esac
+
+ return $_ret
+}
+
+# ifalias_expand_addr af action addr
+# Expand address range ("N-M") specification in addr.
+# "addr" must not include an address-family keyword.
+# The results will include an address-family keyword.
+#
+ifalias_expand_addr()
+{
+ local _af _action
+
+ _af=$1
+ _action=$2
+ shift 2
+
+ afexists $_af || return
+ ifalias_expand_addr_$_af $_action $*
+}
+
+# ifalias_expand_addr_inet action addr
+# Helper function for ifalias_expand_addr(). Handles IPv4.
+#
+ifalias_expand_addr_inet()
+{
+ local _action _arg _cidr _cidr_addr _exargs
+ local _ipaddr _plen _range _iphead _iptail _iplow _iphigh _ipcount
+ local _retstr _c
+ _action=$1
+ _arg=$2
+ shift 2
+ _exargs=$*
+ _retstr=
+
+ case $_action:$_arg:$_exargs in
+ *:*--*) return ;; # invalid
+ tmp:*[0-9]-[0-9]*:*) # to be expanded
+ _action="alias"
+ ;;
+ *:*[0-9]-[0-9]*:*) # to be expanded
+ ;;
+ tmp:*:*netmask*) # already expanded w/ netmask option
+ echo ${_arg%/[0-9]*} $_exargs && return
+ ;;
+ tmp:*:*) # already expanded w/o netmask option
+ echo $_arg $_exargs && return
+ ;;
+ *:*:*netmask*) # already expanded w/ netmask option
+ echo inet ${_arg%/[0-9]*} $_exargs && return
+ ;;
+ *:*:*) # already expanded w/o netmask option
+ echo inet $_arg $_exargs && return
+ ;;
+ esac
+
+ for _cidr in $_arg; do
+ _ipaddr=${_cidr%%/*}
+ _plen=${_cidr##*/}
+ # When subnet prefix length is not specified, use /32.
+ case $_plen in
+ $_ipaddr) _plen=32 ;; # "/" character not found
+ esac
+
+ OIFS=$IFS
+ IFS=. set -- $_ipaddr
+ _range=
+ _iphead=
+ _iptail=
+ for _c in $@; do
+ case $_range:$_c in
+ :[0-9]*-[0-9]*)
+ _range=$_c
+ ;;
+ :*)
+ _iphead="${_iphead}${_iphead:+.}${_c}"
+ ;;
+ *:*)
+ _iptail="${_iptail}${_iptail:+.}${_c}"
+ ;;
+ esac
+ done
+ IFS=$OIFS
+ _iplow=${_range%-*}
+ _iphigh=${_range#*-}
+
+ # clear netmask when removing aliases
+ if [ "$_action" = "-alias" ]; then
+ _plen=""
+ fi
+
+ _ipcount=$_iplow
+ while [ "$_ipcount" -le "$_iphigh" ]; do
+ _retstr="${_retstr} ${_iphead}${_iphead:+.}${_ipcount}${_iptail:+.}${_iptail}${_plen:+/}${_plen}"
+ if [ $_ipcount -gt $(($_iplow + $netif_ipexpand_max)) ]; then
+ warn "Range specification is too large (${_iphead}${_iphead:+.}${_iplow}${_iptail:+.}${_iptail}-${_iphead}${_iphead:+.}${_iphigh}${_iptail:+.}${_iptail}). ${_iphead}${_iphead:+.}${_iplow}${_iptail:+.}${_iptail}-${_iphead}${_iphead:+.}${_ipcount}${_iptail:+.}${_iptail} was processed. Increase \$netif_ipexpand_max in rc.conf."
+ break
+ else
+ _ipcount=$(($_ipcount + 1))
+ fi
+ # Forcibly set /32 for remaining aliases.
+ _plen=32
+ done
+ done
+
+ for _c in $_retstr; do
+ ifalias_expand_addr_inet $_action $_c $_exargs
+ done
+}
+
+# ifalias_expand_addr_inet6 action addr
+# Helper function for ifalias_expand_addr(). Handles IPv6.
+#
+ifalias_expand_addr_inet6()
+{
+ local _action _arg _cidr _cidr_addr _exargs
+ local _ipaddr _plen _ipleft _ipright _iplow _iphigh _ipcount
+ local _ipv4part
+ local _retstr _c
+ _action=$1
+ _arg=$2
+ shift 2
+ _exargs=$*
+ _retstr=
+
+ case $_action:$_arg:$_exargs in
+ *:*--*:*) return ;; # invalid
+ tmp:*[0-9a-zA-Z]-[0-9a-zA-Z]*:*)# to be expanded
+ _action="alias"
+ ;;
+ *:*[0-9a-zA-Z]-[0-9a-zA-Z]*:*) # to be expanded
+ ;;
+ tmp:*:*prefixlen*) # already expanded w/ prefixlen option
+ echo ${_arg%/[0-9]*} $_exargs && return
+ ;;
+ tmp:*:*) # already expanded w/o prefixlen option
+ echo $_arg $_exargs && return
+ ;;
+ *:*:*prefixlen*) # already expanded w/ prefixlen option
+ echo inet6 ${_arg%/[0-9]*} $_exargs && return
+ ;;
+ *:*:*) # already expanded w/o prefixlen option
+ echo inet6 $_arg $_exargs && return
+ ;;
+ esac
+
+ for _cidr in $_arg; do
+ _ipaddr="${_cidr%%/*}"
+ _plen="${_cidr##*/}"
+
+ case $_action:$_ipaddr:$_cidr in
+ -alias:*:*) unset _plen ;;
+ *:$_cidr:$_ipaddr) unset _plen ;;
+ esac
+
+ if [ "${_ipaddr%:*.*.*.*}" = "$_ipaddr" ]; then
+ # Handle !v4mapped && !v4compat addresses.
+
+ # The default prefix length is 64.
+ case $_ipaddr:$_cidr in
+ $_cidr:$_ipaddr) _plen="64" ;;
+ esac
+ _ipleft=${_ipaddr%-*}
+ _ipright=${_ipaddr#*-}
+ _iplow=${_ipleft##*:}
+ _iphigh=${_ipright%%:*}
+ _ipleft=${_ipleft%:*}
+ _ipright=${_ipright#*:}
+
+ if [ "$_iphigh" = "$_ipright" ]; then
+ unset _ipright
+ else
+ _ipright=:$_ipright
+ fi
+
+ if [ -n "$_iplow" -a -n "$_iphigh" ]; then
+ _iplow=$((0x$_iplow))
+ _iphigh=$((0x$_iphigh))
+ _ipcount=$_iplow
+ while [ $_ipcount -le $_iphigh ]; do
+ _r=`printf "%s:%04x%s%s" \
+ $_ipleft $_ipcount $_ipright \
+ ${_plen:+/}$_plen`
+ _retstr="$_retstr $_r"
+ if [ $_ipcount -gt $(($_iplow + $netif_ipexpand_max)) ]
+ then
+ warn "Range specification is too large $(printf '(%s:%x%s-%s:%x%s)' "$_ipleft" "$_iplow" "$_ipright" "$_ipleft" "$_iphigh" "$_ipright"). $(printf '%s:%x%s-%s:%x%s' "$_ipleft" "$_iplow" "$_ipright" "$_ipleft" "$_ipcount" "$_ipright") was processed. Increase \$netif_ipexpand_max in rc.conf."
+ break
+ else
+ _ipcount=$(($_ipcount + 1))
+ fi
+ done
+ else
+ _retstr="${_ipaddr}${_plen:+/}${_plen}"
+ fi
+
+ for _c in $_retstr; do
+ ifalias_expand_addr_inet6 $_action $_c $_exargs
+ done
+ else
+ # v4mapped/v4compat should handle as an IPv4 alias
+ _ipv4part=${_ipaddr##*:}
+
+ # Adjust prefix length if any. If not, set the
+ # default prefix length as 32.
+ case $_ipaddr:$_cidr in
+ $_cidr:$_ipaddr) _plen=32 ;;
+ *) _plen=$(($_plen - 96)) ;;
+ esac
+
+ _retstr=`ifalias_expand_addr_inet \
+ tmp ${_ipv4part}${_plen:+/}${_plen}`
+ for _c in $_retstr; do
+ ifalias_expand_addr_inet $_action $_c $_exargs
+ done
+ fi
+ done
+}
+
+# ifalias_af_common_handler if af action args
+# Helper function for ifalias_af_common().
+#
+ifalias_af_common_handler()
+{
+ local _ret _if _af _action _args _c _tmpargs
+
+ _ret=1
+ _if=$1
+ _af=$2
+ _action=$3
+ shift 3
+ _args=$*
+
+ case $_args in
+ ${_af}[[:space:]]*) ;;
+ *) return ;;
+ esac
+
+ # link(ether) does not support address removal.
+ case $_af:$_action in
+ link:-alias|ether:-alias) return ;;
+ esac
+
+ _tmpargs=
+ for _c in $_args; do
+ case $_c in
+ ${_af})
+ case $_tmpargs in
+ ${_af}[[:space:]]*[0-9a-fA-F]-*)
+ ifalias_af_common_handler $_if $_af $_action \
+ `ifalias_expand_addr $_af $_action ${_tmpargs#${_af}[[:space:]]}`
+ ;;
+ ${_af}[[:space:]]*)
+ ${IFCONFIG_CMD} $_if $_tmpargs $_action && _ret=0
+ ;;
+ esac
+ _tmpargs=$_af
+ ;;
+ *)
+ _tmpargs="$_tmpargs $_c"
+ ;;
+ esac
+ done
+ # Process the last component if any.
+ if [ -n "${_tmpargs}" ]; then
+ case $_tmpargs in
+ ${_af}[[:space:]]pass[[:space:]]*)
+ ${IFCONFIG_CMD} $_if $_tmpargs $_action && _ret=0
+ ;;
+ ${_af}[[:space:]]*[0-9a-fA-F]-*)
+ ifalias_af_common_handler $_if $_af $_action \
+ `ifalias_expand_addr $_af $_action ${_tmpargs#${_af}[[:space:]]}`
+ ;;
+ ${_af}[[:space:]]*)
+ ${IFCONFIG_CMD} $_if $_tmpargs $_action && _ret=0
+ ;;
+ esac
+ fi
+
+ return $_ret
+}
+
+# ifalias_af_common if af action
+# Helper function for ifalias().
+#
+ifalias_af_common()
+{
+ local _ret _if _af _action alias ifconfig_args _aliasn _c _tmpargs _iaf
+ local _vif _punct=".-/+"
+
+ _ret=1
+ _aliasn=
+ _if=$1
+ _af=$2
+ _action=$3
+
+ # Normalize $_if before using it in a pattern to list_vars()
+ ltr "$_if" "$_punct" "_" _vif
+
+ # ifconfig_IF_aliasN which starts with $_af
+ for alias in `list_vars ifconfig_${_vif}_alias[0-9]\* |
+ sort_lite -nk1.$((9+${#_vif}+7))`
+ do
+ eval ifconfig_args=\"\$$alias\"
+ _iaf=
+ case $ifconfig_args in
+ inet[[:space:]]*) _iaf=inet ;;
+ inet6[[:space:]]*) _iaf=inet6 ;;
+ link[[:space:]]*) _iaf=link ;;
+ ether[[:space:]]*) _iaf=ether ;;
+ esac
+
+ case ${_af}:${_action}:${_iaf}:"${ifconfig_args}" in
+ ${_af}:*:${_af}:*)
+ _aliasn="$_aliasn $ifconfig_args"
+ ;;
+ ${_af}:*:"":"")
+ break
+ ;;
+ inet:alias:"":*)
+ _aliasn="$_aliasn inet $ifconfig_args"
+ warn "\$${alias} needs leading" \
+ "\"inet\" keyword for an IPv4 address."
+ esac
+ done
+
+ # backward compatibility: ipv6_ifconfig_IF_aliasN.
+ case $_af in
+ inet6)
+ for alias in `list_vars ipv6_ifconfig_${_vif}_alias[0-9]\* |
+ sort_lite -nk1.$((14+${#_vif}+7))`
+ do
+ eval ifconfig_args=\"\$$alias\"
+ case ${_action}:"${ifconfig_args}" in
+ *:"")
+ break
+ ;;
+ alias:*)
+ _aliasn="${_aliasn} inet6 ${ifconfig_args}"
+ warn "\$${alias} is obsolete. " \
+ "Use ifconfig_${_vif}_aliasN instead."
+ ;;
+ esac
+ done
+ esac
+
+ # backward compatibility: ipv4_addrs_IF.
+ for _tmpargs in `get_if_var $_if ipv4_addrs_IF`; do
+ _aliasn="$_aliasn inet $_tmpargs"
+ done
+
+ # Handle ifconfig_IF_aliases, ifconfig_IF_aliasN, and the others.
+ _tmpargs=
+ for _c in `get_if_var $_if ifconfig_IF_aliases` $_aliasn; do
+ case $_c in
+ inet|inet6|link|ether)
+ case $_tmpargs in
+ ${_af}[[:space:]]*)
+ eval ifalias_af_common_handler $_if $_af $_action $_tmpargs && _ret=0
+ ;;
+ esac
+ _tmpargs=$_c
+ ;;
+ *)
+ _tmpargs="$_tmpargs $_c"
+ esac
+ done
+ # Process the last component
+ case $_tmpargs in
+ ${_af}[[:space:]]*)
+ ifalias_af_common_handler $_if $_af $_action $_tmpargs && _ret=0
+ ;;
+ esac
+
+ return $_ret
+}
+
+# ipv6_prefix_hostid_addr_common if action
+# Add or remove IPv6 prefix + hostid addr on the interface $if
+#
+ipv6_prefix_hostid_addr_common()
+{
+ local _if _action prefix j
+ _if=$1
+ _action=$2
+ prefix=`get_if_var ${_if} ipv6_prefix_IF`
+
+ if [ -n "${prefix}" ]; then
+ for j in ${prefix}; do
+ # The default prefixlen is 64.
+ plen=${j#*/}
+ case $j:$plen in
+ $plen:$j) plen=64 ;;
+ *) j=${j%/*} ;;
+ esac
+
+ # Normalize the last part by removing ":"
+ j=${j%::*}
+ j=${j%:}
+ ${IFCONFIG_CMD} ${_if} inet6 $j:: \
+ prefixlen $plen eui64 ${_action}
+
+ # if I am a router, add subnet router
+ # anycast address (RFC 2373).
+ if checkyesno ipv6_gateway_enable; then
+ ${IFCONFIG_CMD} ${_if} inet6 $j:: \
+ prefixlen $plen ${_action} anycast
+ fi
+ done
+ fi
+}
+
+# ipv6_accept_rtadv_up if
+# Enable accepting Router Advertisement and send Router
+# Solicitation message
+ipv6_accept_rtadv_up()
+{
+ if ipv6_autoconfif $1; then
+ ${IFCONFIG_CMD} $1 inet6 accept_rtadv up
+ if [ -x /sbin/rtsol ]; then
+ /sbin/rtsol ${rtsol_flags} $1
+ fi
+ return 0
+ fi
+ return 1
+}
+
+# ipv6_accept_rtadv_down if
+# Disable accepting Router Advertisement
+ipv6_accept_rtadv_down()
+{
+ if ipv6_autoconfif $1; then
+ ${IFCONFIG_CMD} $1 inet6 -accept_rtadv
+ fi
+}
+
+# ifscript_up if
+# Evaluate a startup script for the $if interface.
+# It returns 0 if a script was found and processed or
+# 1 if no script was found.
+#
+ifscript_up()
+{
+ if [ -r /etc/start_if.$1 ]; then
+ . /etc/start_if.$1
+ return 0
+ else
+ return 1
+ fi
+}
+
+# ifscript_down if
+# Evaluate a shutdown script for the $if interface.
+# It returns 0 if a script was found and processed or
+# 1 if no script was found.
+#
+ifscript_down()
+{
+ if [ -r /etc/stop_if.$1 ]; then
+ . /etc/stop_if.$1
+ return 0
+ else
+ return 1
+ fi
+}
+
+# wlan_up
+# Create IEEE802.11 interfaces.
+#
+wlan_up()
+{
+ local _list _iflist parent child_wlans child create_args debug_flags
+ _list=
+ _iflist=$*
+
+ # Parse wlans_$parent="$child ..."
+ for parent in `set | sed -nE 's/wlans_([a-z]+[a-z0-9]+[0-9]+)=.*/\1/p'`; do
+ child_wlans=`get_if_var $parent wlans_IF`
+ for child in ${child_wlans}; do
+ create_args="wlandev $parent `get_if_var $child create_args_IF`"
+ debug_flags="`get_if_var $child wlandebug_IF`"
+ case $_iflist in
+ ""|$child|$child[[:space:]]*|*[[:space:]]$child[[:space:]]*|*[[:space:]]$child) ;;
+ *) continue ;;
+ esac
+ # Skip if ${child} already exists.
+ if ${IFCONFIG_CMD} $child > /dev/null 2>&1; then
+ continue
+ fi
+ if expr $child : 'wlan[0-9][0-9]*$' >/dev/null 2>&1; then
+ ${IFCONFIG_CMD} $child create ${create_args} && cfg=0
+ else
+ ${IFCONFIG_CMD} wlan create ${create_args} name $child && cfg=0
+ fi
+ if [ $? -eq 0 ]; then
+ _list="$_list $child"
+ fi
+ if [ -n "${debug_flags}" ]; then
+ wlandebug -i $child ${debug_flags}
+ fi
+ done
+ done
+ if [ -n "${_list# }" ]; then
+ echo "Created wlan(4) interfaces: ${_list# }."
+ fi
+ debug "Created wlan(4)s: ${_list# }"
+}
+
+# wlan_down
+# Destroy IEEE802.11 interfaces.
+#
+wlan_down()
+{
+ local _list _iflist parent child_wlans child
+ _list=
+ _iflist=$*
+
+ # Parse wlans_$parent="$child ..."
+ for parent in `set | sed -nE 's/wlans_([a-z]+[a-z0-9]+[0-9]+)=.*/\1/p'`; do
+ child_wlans=`get_if_var $parent wlans_IF`
+ for child in ${child_wlans}; do
+ case $_iflist in
+ ""|$child|$child[[:space:]]*|*[[:space:]]$child[[:space:]]*|*[[:space:]]$child) ;;
+ *) continue ;;
+ esac
+ # Skip if ${child} doesn't exists.
+ if ! ${IFCONFIG_CMD} $child > /dev/null 2>&1; then
+ continue
+ fi
+ ${IFCONFIG_CMD} -n ${child} destroy
+ if [ $? -eq 0 ]; then
+ _list="$_list $child"
+ fi
+ done
+ done
+ if [ -n "${_list# }" ]; then
+ echo "Destroyed wlan(4) interfaces: ${_list# }."
+ fi
+ debug "Destroyed wlan(4)s: ${_list# }"
+}
+
+# clone_up
+# Create cloneable interfaces.
+#
+clone_up()
+{
+ local _list ifn ifopt _iflist _inet6 _n tmpargs
+ _list=
+ _iflist=$*
+
+ # create_args_IF
+ for ifn in ${cloned_interfaces}; do
+ # Parse ifn:ifopt.
+ OIFS=$IFS; IFS=:; set -- $ifn; ifn=$1; ifopt=$2; IFS=$OIFS
+ case $_iflist in
+ ""|$ifn|$ifn[[:space:]]*|*[[:space:]]$ifn[[:space:]]*|*[[:space:]]$ifn) ;;
+ *) continue ;;
+ esac
+ case $ifn in
+ epair[0-9]*)
+ # epair(4) uses epair[0-9] for creation and
+ # epair[0-9][ab] for configuration.
+ #
+ # Skip if ${ifn}a or ${ifn}b already exist.
+ if ${IFCONFIG_CMD} ${ifn}a > /dev/null 2>&1; then
+ continue
+ elif ${IFCONFIG_CMD} ${ifn}b > /dev/null 2>&1; then
+ continue
+ fi
+ ${IFCONFIG_CMD} ${ifn} create \
+ `get_if_var ${ifn} create_args_IF`
+ if [ $? -eq 0 ]; then
+ _list="$_list ${ifn}a ${ifn}b"
+ fi
+ ;;
+ *)
+ # Skip if ${ifn} already exists.
+ if ${IFCONFIG_CMD} $ifn > /dev/null 2>&1; then
+ continue
+ fi
+ ${IFCONFIG_CMD} ${ifn} create \
+ `get_if_var ${ifn} create_args_IF`
+ if [ $? -eq 0 ]; then
+ _list="$_list $ifn"
+ fi
+ esac
+ done
+ for ifn in ${gif_interfaces}; do
+ # Parse ifn:ifopt.
+ OIFS=$IFS; IFS=:; set -- $ifn; ifn=$1; ifopt=$2; IFS=$OIFS
+ case $_iflist in
+ ""|$ifn|$ifn[[:space:]]*|*[[:space:]]$ifn[[:space:]]*|*[[:space:]]$ifn) ;;
+ *) continue ;;
+ esac
+ # Skip if ifn already exists.
+ if ${IFCONFIG_CMD} $ifn > /dev/null 2>&1; then
+ continue
+ fi
+ case $ifn in
+ gif[0-9]*)
+ ${IFCONFIG_CMD} $ifn create
+ ;;
+ *)
+ _n=$(${IFCONFIG_CMD} gif create)
+ ${IFCONFIG_CMD} $_n name $ifn
+ ;;
+ esac
+ if [ $? -eq 0 ]; then
+ _list="$_list $ifn"
+ tmpargs=$(get_if_var $ifn gifconfig_IF)
+ _inet6=''
+ case "$tmpargs" in
+ '')
+ ;;
+ inet6[[:space:]]*)
+ tmpargs=${tmpargs#inet6}
+ _inet6=inet6
+ # FALLTHROUGH
+ ;&
+ *)
+ ${IFCONFIG_CMD} $ifn $_inet6 tunnel $tmpargs
+ ;;
+ esac
+ fi
+ done
+ if [ -n "${_list# }" ]; then
+ echo "Created clone interfaces: ${_list# }."
+ fi
+ debug "Cloned: ${_list# }"
+}
+
+# clone_down
+# Destroy cloned interfaces. Destroyed interfaces are echoed to
+# standard output.
+#
+clone_down()
+{
+ local _list ifn _difn ifopt _iflist _sticky
+ _list=
+ _iflist=$*
+
+ : ${cloned_interfaces_sticky:=NO}
+ if checkyesno cloned_interfaces_sticky; then
+ _sticky=1
+ else
+ _sticky=0
+ fi
+ for ifn in ${cloned_interfaces} ${gif_interfaces}; do
+ # Parse ifn:ifopt.
+ OIFS=$IFS; IFS=:; set -- $ifn; ifn=$1; ifopt=$2; IFS=$OIFS
+ case $ifopt:$_sticky in
+ sticky:*) continue ;; # :sticky => not destroy
+ nosticky:*) ;; # :nosticky => destroy
+ *:1) continue ;; # global sticky knob == 1
+ esac
+ case $_iflist in
+ ""|$ifn|$ifn[[:space:]]*|*[[:space:]]$ifn[[:space:]]*|*[[:space:]]$ifn) ;;
+ *) continue ;;
+ esac
+ case $ifn in
+ epair[0-9]*)
+ # Note: epair(4) uses epair[0-9] for removal and
+ # epair[0-9][ab] for configuration.
+ #
+ # Skip if both of ${ifn}a and ${ifn}b do not exist.
+ if ${IFCONFIG_CMD} ${ifn}a > /dev/null 2>&1; then
+ _difn=${ifn}a
+ elif ${IFCONFIG_CMD} ${ifn}b > /dev/null 2>&1; then
+ _difn=${ifn}b
+ else
+ continue
+ fi
+ ${IFCONFIG_CMD} -n $_difn destroy
+ if [ $? -eq 0 ]; then
+ _list="$_list ${ifn}a ${ifn}b"
+ fi
+ ;;
+ *)
+ # Skip if ifn does not exist.
+ if ! ${IFCONFIG_CMD} $ifn > /dev/null 2>&1; then
+ continue
+ fi
+ ${IFCONFIG_CMD} -n ${ifn} destroy
+ if [ $? -eq 0 ]; then
+ _list="$_list $ifn"
+ fi
+ ;;
+ esac
+ done
+ if [ -n "${_list# }" ]; then
+ echo "Destroyed clone interfaces: ${_list# }."
+ fi
+ debug "Destroyed clones: ${_list# }"
+}
+
+# childif_create
+# Create and configure child interfaces. Return 0 if child
+# interfaces are created.
+#
+childif_create()
+{
+ local cfg child child_vlans create_args debug_flags ifn i
+ cfg=1
+ ifn=$1
+
+ # Create vlan interfaces
+ child_vlans=`get_if_var $ifn vlans_IF`
+
+ if [ -n "${child_vlans}" ]; then
+ load_kld if_vlan
+ fi
+
+ for child in ${child_vlans}; do
+ if expr $child : '[1-9][0-9]*$' >/dev/null 2>&1; then
+ child="${ifn}.${child}"
+ create_args=`get_if_var $child create_args_IF`
+ ${IFCONFIG_CMD} $child create ${create_args} && cfg=0
+ else
+ create_args="vlandev $ifn `get_if_var $child create_args_IF`"
+ if expr $child : 'vlan[0-9][0-9]*$' >/dev/null 2>&1; then
+ ${IFCONFIG_CMD} $child create ${create_args} && cfg=0
+ else
+ i=`${IFCONFIG_CMD} vlan create ${create_args}`
+ ${IFCONFIG_CMD} $i name $child && cfg=0
+ fi
+ fi
+ if autoif $child; then
+ ifn_start $child
+ fi
+ done
+
+ return ${cfg}
+}
+
+# childif_destroy
+# Destroy child interfaces.
+#
+childif_destroy()
+{
+ local cfg child child_vlans ifn
+ cfg=1
+
+ child_vlans=`get_if_var $ifn vlans_IF`
+ for child in ${child_vlans}; do
+ if expr $child : '[1-9][0-9]*$' >/dev/null 2>&1; then
+ child="${ifn}.${child}"
+ fi
+ if ! ifexists $child; then
+ continue
+ fi
+ ${IFCONFIG_CMD} -n $child destroy && cfg=0
+ done
+
+ return ${cfg}
+}
+
+# ng_mkpeer
+# Create netgraph nodes.
+#
+ng_mkpeer()
+{
+ ngctl -f - 2> /dev/null <<EOF
+mkpeer $*
+msg dummy nodeinfo
+EOF
+}
+
+# ng_create_one
+# Create netgraph nodes.
+#
+ng_create_one()
+{
+ local t
+
+ ng_mkpeer $* | while read line; do
+ t=`expr "${line}" : '.* name="\([a-z]*[0-9]*\)" .*'`
+ if [ -n "${t}" ]; then
+ echo ${t}
+ return
+ fi
+ done
+}
+
+# ifnet_rename [ifname]
+# Rename interfaces if ifconfig_IF_name is defined.
+#
+ifnet_rename()
+{
+ local _if _ifname
+
+ # ifconfig_IF_name
+ for _if in ${*:-$(${IFCONFIG_CMD} -l)}; do
+ _ifname=`get_if_var $_if ifconfig_IF_name`
+ if [ ! -z "$_ifname" ]; then
+ ${IFCONFIG_CMD} $_if name $_ifname
+ fi
+ done
+
+ return 0
+}
+
+# list_net_interfaces type
+# List all network interfaces. The type of interface returned
+# can be controlled by the type argument. The type
+# argument can be any of the following:
+# nodhcp - all interfaces, excluding DHCP configured interfaces
+# dhcp - list only DHCP configured interfaces
+# noautoconf - all interfaces, excluding IPv6 Stateless
+# Address Autoconf configured interfaces
+# autoconf - list only IPv6 Stateless Address Autoconf
+# configured interfaces
+# If no argument is specified all network interfaces are output.
+# Note that the list will include cloned interfaces if applicable.
+# Cloned interfaces must already exist to have a chance to appear
+# in the list if ${network_interfaces} is set to `auto'.
+#
+list_net_interfaces()
+{
+ local type _tmplist _list _autolist _lo _if
+ type=$1
+
+ # Get a list of ALL the interfaces and make lo0 first if it's there.
+ #
+ _tmplist=
+ case ${network_interfaces} in
+ [Aa][Uu][Tt][Oo])
+ _autolist="`${IFCONFIG_CMD} -l`"
+ _lo=
+ for _if in ${_autolist} ; do
+ if autoif $_if; then
+ if [ "$_if" = "lo0" ]; then
+ _lo="lo0 "
+ else
+ _tmplist="${_tmplist} ${_if}"
+ fi
+ fi
+ done
+ _tmplist="${_lo}${_tmplist# }"
+ ;;
+ *)
+ for _if in ${network_interfaces} ${cloned_interfaces}; do
+ # epair(4) uses epair[0-9] for creation and
+ # epair[0-9][ab] for configuration.
+ case $_if in
+ epair[0-9]*)
+ _tmplist="$_tmplist ${_if}a ${_if}b"
+ ;;
+ *)
+ _tmplist="$_tmplist $_if"
+ ;;
+ esac
+ done
+ #
+ # lo0 is effectively mandatory, so help prevent foot-shooting
+ #
+ case "$_tmplist" in
+ lo0|'lo0 '*|*' lo0'|*' lo0 '*)
+ # This is fine, do nothing
+ _tmplist="${_tmplist# }"
+ ;;
+ *)
+ _tmplist="lo0 ${_tmplist# }"
+ ;;
+ esac
+ ;;
+ esac
+
+ _list=
+ case "$type" in
+ nodhcp)
+ for _if in ${_tmplist} ; do
+ if ! dhcpif $_if && \
+ [ -n "`_ifconfig_getargs $_if`" ]; then
+ _list="${_list# } ${_if}"
+ fi
+ done
+ ;;
+ dhcp)
+ for _if in ${_tmplist} ; do
+ if dhcpif $_if; then
+ _list="${_list# } ${_if}"
+ fi
+ done
+ ;;
+ noautoconf)
+ for _if in ${_tmplist} ; do
+ if ! ipv6_autoconfif $_if && \
+ [ -n "`_ifconfig_getargs $_if ipv6`" ]; then
+ _list="${_list# } ${_if}"
+ fi
+ done
+ ;;
+ autoconf)
+ for _if in ${_tmplist} ; do
+ if ipv6_autoconfif $_if; then
+ _list="${_list# } ${_if}"
+ fi
+ done
+ ;;
+ *)
+ _list=${_tmplist}
+ ;;
+ esac
+
+ echo $_list
+
+ return 0
+}
+
+# get_default_if -address_family
+# Get the interface of the default route for the given address family.
+# The -address_family argument must be suitable passing to route(8).
+#
+get_default_if()
+{
+ local routeget oldifs defif line
+ defif=
+ oldifs="$IFS"
+ IFS="
+"
+ for line in `route -n get $1 default 2>/dev/null`; do
+ case $line in
+ *interface:*)
+ defif=${line##*: }
+ ;;
+ esac
+ done
+ IFS=${oldifs}
+
+ echo $defif
+}
+
+# hexdigit arg
+# Echo decimal number $arg (single digit) in hexadecimal format.
+hexdigit()
+{
+ printf '%x\n' "$1"
+}
+
+# hexprint arg
+# Echo decimal number $arg (multiple digits) in hexadecimal format.
+hexprint()
+{
+ printf '%x\n' "$1"
+}
+
+is_wired_interface()
+{
+ local media
+
+ case `${IFCONFIG_CMD} $1 2>/dev/null` in
+ *media:?Ethernet*) media=Ethernet ;;
+ esac
+
+ test "$media" = "Ethernet"
+}
+
+# network6_getladdr if [flag]
+# Echo link-local address from $if if any.
+# If flag is defined, tentative ones will be excluded.
+network6_getladdr()
+{
+ local _if _flag proto addr rest
+ _if=$1
+ _flag=$2
+
+ ${IFCONFIG_CMD} $_if inet6 2>/dev/null | while read proto addr rest; do
+ case "${proto}/${addr}/${_flag}/${rest}" in
+ inet6/fe80::*//*)
+ echo ${addr}
+ ;;
+ inet6/fe80:://*tentative*) # w/o flag
+ sleep `${SYSCTL_N} net.inet6.ip6.dad_count`
+ network6_getladdr $_if $_flags
+ ;;
+ inet6/fe80::/*/*tentative*) # w/ flag
+ echo ${addr}
+ ;;
+ *)
+ continue
+ ;;
+ esac
+
+ return
+ done
+}
diff --git a/libexec/rc/pccard_ether b/libexec/rc/pccard_ether
new file mode 100755
index 000000000000..957983e55a8e
--- /dev/null
+++ b/libexec/rc/pccard_ether
@@ -0,0 +1,147 @@
+#!/bin/sh -
+#
+#
+# pccard_ether interfacename [start|stop|restart]
+#
+# example: pccard_ether fxp0 start
+#
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="pccard_ether"
+start_precmd="checkauto"
+start_cmd="pccard_ether_start"
+stop_precmd="checkauto"
+stop_cmd="pccard_ether_stop"
+restart_precmd="checkauto"
+restart_cmd="pccard_ether_restart"
+startchildren_cmd="pccard_ether_startchildren"
+stopchildren_cmd="pccard_ether_stopchildren"
+extra_commands="startchildren stopchildren"
+
+setup_routes()
+{
+ # Add default route into $static_routes
+ case ${defaultrouter} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ static_routes="default ${static_routes}"
+ route_default="default ${defaultrouter}"
+ ;;
+ esac
+
+ # Add private route for this interface into $static_routes
+ eval ifx_routes=\$static_routes_${ifn}
+ if [ -n "${ifx_routes}" ]; then
+ static_routes="${ifx_routes} ${static_routes}"
+ fi
+
+ # Set up any static routes if specified
+ if [ -n "${static_routes}" ]; then
+ for i in ${static_routes}; do
+ eval route_args=\$route_${i}
+ route add ${route_args}
+ done
+ fi
+}
+
+remove_routes()
+{
+ # Delete static route if specified
+ eval ifx_routes=\$static_routes_${ifn}
+ if [ -n "${ifx_routes}" ]; then
+ for i in ${ifx_routes}; do
+ eval route_args=\$route_${i}
+ route delete ${route_args}
+ done
+ fi
+}
+
+checkauto()
+{
+ if [ -z "$rc_force" ]; then
+ # Ignore interfaces with the NOAUTO keyword
+ autoif $ifn || exit 0
+ fi
+}
+
+pccard_ether_start()
+{
+ ifisup $ifn
+ case $? in
+ 0) # Interface is already up, so ignore it.
+ if [ -z "$rc_force"]; then
+ exit 0
+ fi
+ ;;
+ 2) # Interface does not exist.
+ exit 1
+ ;;
+ esac
+
+ /etc/rc.d/netif quietstart $ifn
+
+ # Do route configuration if needed.
+ # XXX: should probably do this by calling rc.d/routing.
+ if [ -n "`ifconfig_getargs $ifn`" ]; then
+ if ! dhcpif $ifn; then
+ setup_routes
+ fi
+ fi
+
+ # XXX: IPv6 setup should be done in some way.
+}
+
+pccard_ether_stop()
+{
+ if [ -n "`ifconfig_getargs $ifn`" ]; then
+ if ! dhcpif $ifn; then
+ remove_routes
+ fi
+ fi
+
+ /etc/rc.d/netif quietstop $ifn
+
+ # clean ARP table
+ ifexists $ifn && arp -d -i $ifn -a
+}
+
+pccard_ether_restart()
+{
+ # Hand implemented because the default implementation runs
+ # the equivalent of "$0 start; $0 stop" and this script
+ # doesn't support that syntax
+ pccard_ether_stop
+ pccard_ether_start
+}
+
+pccard_ether_startchildren()
+{
+ for child in `get_if_var $ifn wlans_IF`; do
+ if ifexists $child; then
+ continue
+ fi
+ /etc/rc.d/netif quietstart $child
+ done
+}
+
+pccard_ether_stopchildren()
+{
+ for child in `get_if_var $ifn wlans_IF`; do
+ /etc/rc.d/netif quietstop $child
+ done
+}
+
+ifn=$1
+shift
+if [ -z "$*" ]; then
+ args="start"
+else
+ args=$*
+fi
+
+load_rc_config pccard_ether
+load_rc_config network
+run_rc_command $args
diff --git a/libexec/rc/rc b/libexec/rc/rc
new file mode 100644
index 000000000000..db3c3e20ab44
--- /dev/null
+++ b/libexec/rc/rc
@@ -0,0 +1,152 @@
+#!/bin/sh
+#
+# Copyright (c) 2000-2004 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# System startup script run by init on autoboot
+# or after single-user.
+# Output and error are redirected to console by init,
+# and the console is the controlling terminal.
+
+# Note that almost all of the user-configurable behavior is no longer in
+# this file, but rather in /etc/defaults/rc.conf. Please check that file
+# first before contemplating any changes here. If you do need to change
+# this file for some reason, we would like to know about it.
+
+stty status '^T' 2> /dev/null
+
+# Set shell to ignore SIGINT (2), but not children;
+# shell catches SIGQUIT (3) and returns to single user.
+#
+trap : 2
+trap "echo 'Boot interrupted'; exit 1" 3
+
+HOME=/
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+export HOME PATH
+
+if [ "$1" = autoboot ]; then
+ autoboot=yes
+ _boot="faststart"
+ rc_fast=yes # run_rc_command(): do fast booting
+else
+ autoboot=no
+ _boot="quietstart"
+fi
+
+_localbase=`/sbin/sysctl -n user.localbase 2> /dev/null`
+
+dlv=`/sbin/sysctl -n vfs.nfs.diskless_valid 2> /dev/null`
+if [ ${dlv:=0} -ne 0 -o -f /etc/diskless ]; then
+ sh /etc/rc.initdiskless
+fi
+
+# Run these after determining whether we are booting diskless in order
+# to minimize the number of files that are needed on a diskless system,
+# and to make the configuration file variables available to rc itself.
+#
+# -o verify has no effect if mac_veriexec is not active
+set -o verify
+. /etc/rc.subr
+set +o verify
+load_rc_config $rc_config_xtra
+
+if have DebugOn; then
+ # allow DEBUG_SH to be set from loader prompt
+ export DEBUG_SH=${DEBUG_SH:-$(kenv -q DEBUG_SH)}
+fi
+
+# If we receive a SIGALRM, re-source /etc/rc.conf; this allows rc.d
+# scripts to perform "boot-time configuration" including enabling and
+# disabling rc.d scripts which appear later in the boot order.
+trap "_rc_conf_loaded=false; load_rc_config" ALRM
+
+skip="-s nostart"
+if check_jail jailed; then
+ skip="$skip -s nojail"
+ if ! check_jail vnet; then
+ skip="$skip -s nojailvnet"
+ fi
+fi
+
+# If the firstboot sentinel doesn't exist, we want to skip firstboot scripts.
+if ! [ -e ${firstboot_sentinel} ]; then
+ skip_firstboot="-s firstboot"
+fi
+
+# Do a first pass to get everything up to $early_late_divider so that
+# we can do a second pass that includes $local_startup directories
+#
+unset system_rc
+find_system_scripts
+files=`rcorder ${skip} ${skip_firstboot} ${system_rc} 2>/dev/null`
+run_rc_scripts --break ${early_late_divider} ${rc_early_flags} $files
+
+unset files local_rc system_rc
+
+# Now that disks are mounted, for each dir in $local_startup
+# search for init scripts that use the new rc.d semantics.
+#
+case ${local_startup} in
+[Nn][Oo] | '') ;;
+*) find_local_scripts_new ;;
+esac
+
+# The firstboot sentinel might be on a newly mounted filesystem; look for it
+# again and unset skip_firstboot if we find it.
+if [ -e ${firstboot_sentinel} ]; then
+ skip_firstboot=""
+fi
+
+find_system_scripts
+files=`rcorder ${skip} ${skip_firstboot} ${system_rc} ${local_rc} 2>/dev/null`
+run_rc_scripts ${rc_late_flags} $files
+unset files local_rc system_rc
+
+# allow for more complicated setups
+if have run_rc_scripts_final; then
+ run_rc_scripts_final
+fi
+
+# Remove the firstboot sentinel, and reboot if it was requested.
+# Be a bit paranoid about removing it to handle the common failure
+# modes since the consequence of failure can be big.
+# Note: this assumes firstboot_sentinel is on / when we have
+# a read-only /, or that it is on media that's writable.
+if [ -e ${firstboot_sentinel} ]; then
+ checkyesno root_rw_mount || mount -uw /
+ chflags -R 0 ${firstboot_sentinel}
+ rm -rf ${firstboot_sentinel}
+ if [ -e ${firstboot_sentinel}-reboot ]; then
+ chflags -R 0 ${firstboot_sentinel}-reboot
+ rm -rf ${firstboot_sentinel}-reboot
+ checkyesno root_rw_mount || mount -ur /
+ kill -INT 1
+ fi
+ checkyesno root_rw_mount || mount -ur /
+fi
+
+echo ''
+date
+exit 0
diff --git a/libexec/rc/rc.bsdextended b/libexec/rc/rc.bsdextended
new file mode 100644
index 000000000000..01222f1e78b4
--- /dev/null
+++ b/libexec/rc/rc.bsdextended
@@ -0,0 +1,137 @@
+#!/bin/sh
+#
+# Copyright (c) 2004 Tom Rhodes
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+####
+# Sample startup policy for the mac_bsdextended(4) security module.
+#
+# Suck in the system configuration variables.
+####
+if [ -z "${source_rc_confs_defined}" ]; then
+ if [ -r /etc/defaults/rc.conf ]; then
+ . /etc/defaults/rc.conf
+ source_rc_confs
+ elif [ -r /etc/rc.conf ]; then
+ . /etc/rc.conf
+ fi
+fi
+
+####
+# Set ugidfw(8) to CMD:
+####
+CMD=/usr/sbin/ugidfw
+
+####
+# WARNING: recommended reading is the handbook's MAC
+# chapter and the ugidfw(8) manual page. You can
+# lock yourself out of the system very quickly by setting
+# incorrect values here. These are only examples.
+####
+
+####
+# Build a generic list of rules here, these should be
+# modified before using this script.
+#
+# For apache to read user files, the ruleadd must give
+# it permissions by default.
+####
+#${CMD} add subject uid 80 object not uid 80 mode rxws;
+#${CMD} add subject gid 80 object not gid 80 mode rxws;
+
+####
+# majordomo compat:
+#${CMD} add subject uid 54 object not uid 54 mode rxws;
+#${CMD} add subject gid 26 object gid 54 mode rxws;
+
+####
+# This is for root:
+${CMD} add subject uid 0 object not uid 0 mode arxws;
+${CMD} add subject gid 0 object not gid 0 mode arxws;
+
+####
+# And for majordomo:
+#${CMD} add subject uid 54 object not uid 54 mode rxws;
+#${CMD} add subject gid 54 object not gid 54 mode rxws;
+
+####
+# And for bin:
+${CMD} add subject uid 3 object not uid 3 mode rxws;
+${CMD} add subject gid 7 object not gid 7 mode rxws;
+
+####
+# And for mail/pop:
+#${CMD} add subject uid 68 object not uid 68 mode rxws;
+#${CMD} add subject gid 6 object not gid 6 mode arxws;
+
+####
+# And for smmsp:
+${CMD} add subject uid 25 object not uid 25 mode rxws;
+${CMD} add subject gid 25 object not gid 25 mode rxws;
+
+####
+# And for mailnull:
+${CMD} add subject uid 26 object not uid 26 mode rxws;
+${CMD} add subject gid 26 object not gid 26 mode rxws;
+
+####
+# For cyrus:
+#${CMD} add subject uid 60 object not uid 60 mode rxws;
+#${CMD} add subject gid 60 object not gid 60 mode rxws;
+
+####
+# For stunnel:
+#${CMD} add subject uid 1018 object not uid 1018 mode rxws;
+#${CMD} add subject gid 1018 object not gid 1018 mode rxws;
+
+####
+# For the nobody account:
+${CMD} add subject uid 65534 object not uid 65534 mode rxws;
+${CMD} add subject gid 65534 object not gid 65534 mode rxws;
+
+####
+# NOTICE: The next script adds a rule to allow
+# access their mailbox which is owned by GID `6'.
+# Removing this will give mailbox lock issues.
+for x in `awk -F: '($3 >= 1001) && ($3 != 65534) { print $1 }' /etc/passwd`;
+ do ${CMD} add subject uid $x object gid 6 mode arwxs;
+done;
+
+####
+# Use some script to get a list of users and
+# add all users to mode n for all other users. This
+# will isolate all users from other user home directories while
+# permitting them to use commands and browse the system.
+for x in `awk -F: '($3 >= 1001) && ($3 != 65534) { print $1 }' /etc/passwd`;
+ do ${CMD} add subject not uid $x object uid $x mode n;
+done;
+
+###
+# Do the same thing but only for group ids in place of
+# user IDs.
+for x in `awk -F: '($3 >= 1001) && ($3 != 65534) { print $3 }' /etc/passwd`;
+ do ${CMD} add subject not gid $x object uid $x mode n;
+done;
diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf
new file mode 100644
index 000000000000..ada9094360f6
--- /dev/null
+++ b/libexec/rc/rc.conf
@@ -0,0 +1,794 @@
+#!/bin/sh
+
+# This is rc.conf - a file full of useful variables that you can set
+# to change the default startup behavior of your system. You should
+# not edit this file! Put any overrides into one of the ${rc_conf_files}
+# instead and you will be able to update these defaults later without
+# spamming your local configuration information.
+#
+# The ${rc_conf_files} files should only contain values which override
+# values set in this file. This eases the upgrade path when defaults
+# are changed and new features are added.
+#
+# All arguments must be in double or single quotes.
+#
+# For a more detailed explanation of all the rc.conf variables, please
+# refer to the rc.conf(5) manual page.
+#
+
+##############################################################
+### Important initial Boot-time options ####################
+##############################################################
+
+# Set default value of _localbase if not previously set
+: ${_localbase:="$(/sbin/sysctl -n user.localbase 2> /dev/null)"}
+: ${_localbase:="/usr/local"}
+
+# rc_debug can't be set here without interfering with rc.subr's setting it
+# when the kenv variable rc.debug is set.
+#rc_debug="NO" # Set to YES to enable debugging output from rc.d
+rc_info="NO" # Enables display of informational messages at boot.
+rc_startmsgs="YES" # Show "Starting foo:" messages at boot
+rcshutdown_timeout="90" # Seconds to wait before terminating rc.shutdown
+precious_machine="NO" # Set to YES to get some guards against mis-directed
+ # shutdown(8) commands
+early_late_divider="FILESYSTEMS" # Script that separates early/late
+ # stages of the boot process. Make sure you know
+ # the ramifications if you change this.
+ # See rc.conf(5) for more details.
+always_force_depends="NO" # Set to check that indicated dependencies are
+ # running during boot (can increase boot time).
+
+apm_enable="NO" # Set to YES to enable APM BIOS functions (or NO).
+apmd_enable="NO" # Run apmd to handle APM event from userland.
+apmd_flags="" # Flags to apmd (if enabled).
+ddb_enable="NO" # Set to YES to load ddb scripts at boot.
+ddb_config="/etc/ddb.conf" # ddb(8) config file.
+devd_enable="YES" # Run devd, to trigger programs on device tree changes.
+devd_flags="" # Additional flags for devd(8).
+devmatch_enable="YES" # Demand load kernel modules based on device ids.
+devmatch_blocklist="" # List of modules (w/o .ko) to exclude from devmatch.
+#kld_list="" # Kernel modules to load after local disks are mounted
+kldxref_enable="YES" # Build linker.hints files with kldxref(8).
+kldxref_clobber="NO" # Overwrite old linker.hints at boot.
+kldxref_module_path="" # Override kern.module_path. A ';'-delimited list.
+powerd_enable="NO" # Run powerd to lower our power usage.
+powerd_flags="" # Flags to powerd (if enabled).
+tmpmfs="AUTO" # Set to YES to always create an mfs /tmp, NO to never
+tmpsize="20m" # Size of mfs /tmp if created
+tmpmfs_flags="-S" # Extra mdmfs options for the mfs /tmp
+utx_enable="YES" # Enable user accounting
+varmfs="AUTO" # Set to YES to always create an mfs /var, NO to never
+varsize="32m" # Size of mfs /var if created
+varmfs_flags="-S" # Extra mount options for the mfs /var
+mfs_type="auto" # "md", "tmpfs", "auto" to prefer tmpfs with md as fallback
+populate_var="AUTO" # Set to YES to always (re)populate /var, NO to never
+cleanvar_enable="YES" # Clean the /var directory
+var_run_enable="YES" # Save/restore /var/run structure at shutdown/reboot
+var_run_autosave="YES" # Only restore /var/run structure at shutdown/reboot
+ # The user is expected to issue service var_run save to
+ # manually save the /var/run mtree
+var_run_mtree="/var/db/mtree/BSD.var-run.mtree"
+ # Where to save /var/run mtree
+local_startup="${_localbase}/etc/rc.d" # startup script dirs.
+script_name_sep=" " # Change if your startup scripts' names contain spaces
+rc_conf_files="/etc/rc.conf /etc/rc.conf.local"
+
+# ZFS support
+zfs_enable="NO" # Set to YES to automatically mount ZFS file systems
+zfskeys_enable="NO" # Set YES to autoload ZFS encryption keys
+zfs_bootonce_activate="NO" # Set YES to make successful bootonce BE permanent
+zpool_reguid="" # Set to zpools for which the GUID should be replaced
+ # upon first boot.
+zpool_upgrade="" # Set to zpools for which the version should be upgraded
+ # upon first boot.
+
+# ZFSD support
+zfsd_enable="NO" # Set to YES to automatically start the ZFS fault
+ # management daemon.
+
+gptboot_enable="YES" # GPT boot success/failure reporting.
+
+# GELI disk encryption configuration.
+geli_devices="" # List of devices to automatically attach in addition to
+ # GELI devices listed in /etc/fstab.
+geli_groups="" # List of groups containing devices to automatically
+ # attach with the same keyfiles and passphrase
+geli_tries="" # Number of times to attempt attaching geli device.
+ # If empty, kern.geom.eli.tries will be used.
+geli_default_flags="" # Default flags for geli(8).
+geli_autodetach="YES" # Automatically detach on last close.
+ # Providers are marked as such when all file systems are
+ # mounted.
+# Example use.
+#geli_devices="da1 mirror/home"
+#geli_da1_flags="-p -k /etc/geli/da1.keys"
+#geli_da1_autodetach="NO"
+#geli_mirror_home_flags="-k /etc/geli/home.keys"
+#geli_groups="storage backup"
+#geli_storage_flags="-k /etc/geli/storage.keys"
+#geli_storage_devices="ada0 ada1"
+#geli_backup_flags="-j /etc/geli/backup.passfile -k /etc/geli/backup.keys"
+#geli_backup_devices="ada2 ada3"
+
+root_rw_mount="YES" # Set to NO to inhibit remounting root read-write.
+root_hold_delay="30" # Time to wait for root mount hold release.
+fsck_flags="-p" # May be changed to -f (or -f -y) to force a full fsck
+fsck_y_enable="NO" # Set to YES to do fsck -y if the initial preen fails.
+fsck_y_flags="-T ffs:-R -T ufs:-R" # Additional flags for fsck -y
+background_fsck="YES" # Attempt to run fsck in the background where possible.
+background_fsck_delay="60" # Time to wait (seconds) before starting the fsck.
+growfs_enable="NO" # Set to YES to attempt to grow the root filesystem on boot
+growfs_swap_size="" # Set to 0 to disable growfs swap, "" to default size,
+ # size in bytes to specify swap size.
+netfs_types="nfs:NFS smbfs:SMB" # Net filesystems.
+extra_netfs_types="NO" # List of network extra filesystem types for delayed
+ # mount at startup (or NO).
+
+##############################################################
+### Network configuration sub-section ######################
+##############################################################
+
+### Basic network and firewall/security options: ###
+hostname="" # Set this!
+hostid_enable="YES" # Set host UUID.
+hostid_file="/etc/hostid" # File with hostuuid.
+hostid_uuidgen_flags="-r" # Flags to uuidgen.
+machine_id_file="/etc/machine-id" # File with machine-id.
+nisdomainname="NO" # Set to NIS domain if using NIS (or NO).
+dhclient_program="/sbin/dhclient" # Path to dhcp client program.
+dhclient_flags="" # Extra flags to pass to dhcp client.
+#dhclient_flags_em0="" # Extra dhclient flags for em0 only
+background_dhclient="NO" # Start dhcp client in the background.
+#background_dhclient_em0="YES" # Start dhcp client on em0 in the background.
+dhclient_arpwait="YES" # Wait for ARP resolution
+synchronous_dhclient="NO" # Start dhclient directly on configured
+ # interfaces during startup.
+defaultroute_delay="30" # Time to wait for a default route on a DHCP interface.
+defaultroute_carrier_delay="5" # Time to wait for carrier while waiting for a default route.
+netif_enable="YES" # Set to YES to initialize network interfaces
+netif_ipexpand_max="2048" # Maximum number of IP addrs in a range spec.
+wpa_supplicant_program="/usr/sbin/wpa_supplicant"
+wpa_supplicant_flags="-s" # Extra flags to pass to wpa_supplicant
+wpa_supplicant_conf_file="/etc/wpa_supplicant.conf"
+#
+firewall_enable="NO" # Set to YES to enable firewall functionality
+firewall_script="/etc/rc.firewall" # Which script to run to set up the firewall
+firewall_type="UNKNOWN" # Firewall type (see /etc/rc.firewall)
+firewall_quiet="NO" # Set to YES to suppress rule display
+firewall_logging="NO" # Set to YES to enable events logging
+firewall_logif="NO" # Set to YES to create logging-pseudo interface
+firewall_flags="" # Flags passed to ipfw when type is a file
+firewall_coscripts="" # List of executables/scripts to run after
+ # firewall starts/stops
+firewall_client_net="192.0.2.0/24" # IPv4 Network address for "client"
+ # firewall.
+#firewall_client_net_ipv6="2001:db8:2:1::/64" # IPv6 network prefix for
+ # "client" firewall.
+firewall_simple_iif="em1" # Inside network interface for "simple"
+ # firewall.
+firewall_simple_inet="192.0.2.16/28" # Inside network address for "simple"
+ # firewall.
+firewall_simple_oif="em0" # Outside network interface for "simple"
+ # firewall.
+firewall_simple_onet="192.0.2.0/28" # Outside network address for "simple"
+ # firewall.
+#firewall_simple_iif_ipv6="em1" # Inside IPv6 network interface for "simple"
+ # firewall.
+#firewall_simple_inet_ipv6="2001:db8:2:800::/56" # Inside IPv6 network prefix
+ # for "simple" firewall.
+#firewall_simple_oif_ipv6="em0" # Outside IPv6 network interface for "simple"
+ # firewall.
+#firewall_simple_onet_ipv6="2001:db8:2:0::/56" # Outside IPv6 network prefix
+ # for "simple" firewall.
+firewall_myservices="" # List of ports/protocols on which this host
+ # offers services for "workstation" firewall.
+firewall_allowservices="" # List of IPs which have access to
+ # $firewall_myservices for "workstation"
+ # firewall.
+firewall_trusted="" # List of IPs which have full access to this
+ # host for "workstation" firewall.
+firewall_logdeny="NO" # Set to YES to log default denied incoming
+ # packets for "workstation" firewall.
+firewall_nologports="135-139,445 1026,1027 1433,1434" # List of TCP/UDP ports
+ # for which denied incoming packets are not
+ # logged for "workstation" firewall.
+firewall_nat_enable="NO" # Enable kernel NAT (if firewall_enable == YES)
+firewall_nat_interface="" # Public interface or IPaddress to use
+firewall_nat_flags="" # Additional configuration parameters
+firewall_nat64_enable="NO" # Enable kernel NAT64 module.
+firewall_nptv6_enable="NO" # Enable kernel NPTv6 module.
+firewall_pmod_enable="NO" # Enable kernel protocols modification module.
+dummynet_enable="NO" # Load the dummynet(4) module
+ipfw_netflow_enable="NO" # Enable netflow logging via ng_netflow
+ip_portrange_first="NO" # Set first dynamically allocated port
+ip_portrange_last="NO" # Set last dynamically allocated port
+ike_enable="NO" # Enable IKE daemon (usually racoon or isakmpd)
+ike_program="${_localbase}/sbin/isakmpd" # Path to IKE daemon
+ike_flags="" # Additional flags for IKE daemon
+ipsec_enable="NO" # Set to YES to run setkey on ipsec_file
+ipsec_file="/etc/ipsec.conf" # Name of config file for setkey
+natd_program="/sbin/natd" # path to natd, if you want a different one.
+natd_enable="NO" # Enable natd (if firewall_enable == YES).
+natd_interface="" # Public interface or IPaddress to use.
+natd_flags="" # Additional flags for natd.
+ipfilter_enable="NO" # Set to YES to enable ipfilter functionality
+ipfilter_program="/sbin/ipf" # where the ipfilter program lives
+ipfilter_rules="/etc/ipf.rules" # rules definition file for ipfilter, see
+ # /usr/src/share/examples/ipfilter for examples
+ipfilter_flags="" # additional flags for ipfilter
+ipfilter_optionlist="" # optionlist for ipf(8) -T
+ippool_enable="NO" # Set to YES to enable ip filter pools
+ippool_program="/sbin/ippool" # where the ippool program lives
+ippool_rules="/etc/ippool.tables" # rules definition file for ippool
+ippool_flags="" # additional flags for ippool
+ipnat_enable="NO" # Set to YES to enable ipnat functionality
+ipnat_program="/sbin/ipnat" # where the ipnat program lives
+ipnat_rules="/etc/ipnat.rules" # rules definition file for ipnat
+ipnat_flags="" # additional flags for ipnat
+ipmon_enable="NO" # Set to YES for ipmon; needs ipfilter or ipnat
+ipmon_program="/sbin/ipmon" # where the ipfilter monitor program lives
+ipmon_flags="-Ds" # typically "-Ds" or "-D /var/log/ipflog"
+ipfs_enable="NO" # Set to YES to enable saving and restoring
+ # of state tables at shutdown and boot
+ipfs_program="/sbin/ipfs" # where the ipfs program lives
+ipfs_flags="" # additional flags for ipfs
+pf_enable="NO" # Set to YES to enable packet filter (pf)
+pf_rules="/etc/pf.conf" # rules definition file for pf (nonexistent
+ # by default)
+pf_program="/sbin/pfctl" # where the pfctl program lives
+pf_flags="" # additional flags for pfctl
+pf_fallback_rules_enable="NO" # fallback if loading ruleset fails
+pf_fallback_rules="block drop log all" # rules to load on pf ruleset failure
+#pf_fallback_rules="block drop log all
+#pass quick on em4" # multi-rule
+pf_fallback_rules_file="/etc/pf-fallback.conf" # rules file on ruleset failure
+pflog_enable="NO" # Set to YES to enable packet filter logging
+pflog_logfile="/var/log/pflog" # where pflogd should store the logfile
+pflog_program="/sbin/pflogd" # where the pflogd program lives
+pflog_flags="" # additional flags for pflogd
+dnctl_enable="NO"
+dnctl_program="/sbin/dnctl"
+dnctl_rules="/etc/dnctl.conf"
+ftpproxy_enable="NO" # Set to YES to enable ftp-proxy(8) for pf
+ftpproxy_flags="" # additional flags for ftp-proxy(8)
+pfsync_enable="NO" # Expose pf state to other hosts for syncing
+pfsync_syncdev="" # Interface for pfsync to work through
+pfsync_syncpeer="" # IP address of pfsync peer host
+pfsync_ifconfig="" # Additional options to ifconfig(8) for pfsync
+tcp_extensions="YES" # Set to NO to turn off RFC1323 extensions.
+log_in_vain="0" # >=1 to log connects to ports w/o listeners.
+tcp_keepalive="YES" # Enable stale TCP connection timeout (or NO).
+tcp_drop_synfin="NO" # Set to YES to drop TCP packets with SYN+FIN
+ # NOTE: this violates the TCP specification
+icmp_drop_redirect="auto" # Set to YES to ignore ICMP REDIRECT packets
+icmp_log_redirect="NO" # Set to YES to log ICMP REDIRECT packets
+network_interfaces="auto" # List of network interfaces (or "auto").
+cloned_interfaces="" # List of cloned network interfaces to create.
+#cloned_interfaces="gif0 gif1 gif2 gif3" # Pre-cloning GENERIC config.
+#ifconfig_lo0="inet 127.0.0.1/8" # default loopback device configuration.
+#ifconfig_lo0_alias0="inet 127.0.0.254/32" # Sample alias entry.
+#ifconfig_em0_ipv6="inet6 2001:db8:1::1 prefixlen 64" # Sample IPv6 addr entry
+#ifconfig_em0_alias0="inet6 2001:db8:2::1 prefixlen 64" # Sample IPv6 alias
+#ifconfig_em0_name="net0" # Change interface name from em0 to net0.
+#vlans_em0="101 vlan0" # vlan(4) interfaces for em0 device
+#create_args_vlan0="vlan 102" # vlan tag for vlan0 device
+#wlans_ath0="wlan0" # wlan(4) interfaces for ath0 device
+#wlandebug_wlan0="scan+auth+assoc" # Set debug flags with wlandebug(8)
+#ipv4_addrs_em0="192.168.0.1/24 192.168.1.1-5/28" # example IPv4 address entry.
+#
+#autobridge_interfaces="bridge0" # List of bridges to check
+#autobridge_bridge0="tap* vlan0" # Interface glob to automatically add to the bridge
+
+# User ppp configuration.
+ppp_enable="NO" # Start user-ppp (or NO).
+ppp_program="/usr/sbin/ppp" # Path to user-ppp program.
+ppp_mode="auto" # Choice of "auto", "ddial", "direct" or "dedicated".
+ # For details see man page for ppp(8). Default is auto.
+ppp_nat="YES" # Use PPP's internal network address translation or NO.
+ppp_profile="papchap" # Which profile to use from /etc/ppp/ppp.conf.
+ppp_user="root" # Which user to run ppp as
+
+# Start multiple instances of ppp at boot time
+#ppp_profile="profile1 profile2 profile3" # Which profiles to use
+#ppp_profile1_mode="ddial" # Override ppp mode for profile1
+#ppp_profile2_nat="NO" # Override nat mode for profile2
+# profile3 uses default ppp_mode and ppp_nat
+
+### Network daemon (miscellaneous) ###
+hostapd_program="/usr/sbin/hostapd"
+hostapd_enable="NO" # Run hostap daemon.
+syslogd_enable="YES" # Run syslog daemon (or NO).
+syslogd_program="/usr/sbin/syslogd" # path to syslogd, if you want a different one.
+syslogd_flags="-s" # Flags to syslogd (if enabled).
+syslogd_oomprotect="YES" # Don't kill syslogd when swap space is exhausted.
+altlog_proglist="" # List of chrooted applicatioins in /var
+inetd_enable="NO" # Run the network daemon dispatcher (YES/NO).
+inetd_program="/usr/sbin/inetd" # path to inetd, if you want a different one.
+inetd_flags="-wW -C 60" # Optional flags to inetd
+iscsid_enable="NO" # iSCSI initiator daemon.
+iscsictl_enable="NO" # iSCSI initiator autostart.
+iscsictl_flags="-Aa" # Optional flags to iscsictl.
+hastd_enable="NO" # Run the HAST daemon (YES/NO).
+hastd_program="/sbin/hastd" # path to hastd, if you want a different one.
+hastd_flags="" # Optional flags to hastd.
+ggated_enable="NO" # Run the ggate daemon (YES/NO).
+ggated_config="/etc/gg.exports" # ggated(8) exports file.
+ggated_flags="" # Extra parameters like which port to bind to.
+ctld_enable="NO" # CAM Target Layer / iSCSI target daemon.
+local_unbound_enable="NO" # Local caching DNS resolver
+local_unbound_oomprotect="YES" # Don't kill local_unbound when swap space is exhausted.
+local_unbound_tls="NO" # Use DNS over TLS
+blacklistd_enable="NO" # Renamed to blocklistd_enable.
+blacklistd_flags="" # Renamed to blocklistd_flags.
+blocklistd_enable="NO" # Run blocklistd daemon (YES/NO).
+blocklistd_flags="" # Optional flags for blocklistd(8).
+resolv_enable="YES" # Enable resolv / resolvconf
+
+#
+# kerberos. Do not run the admin daemons on slave servers
+#
+kdc_enable="NO" # Run a kerberos 5 KDC (or NO).
+kdc_program="" # path to kerberos 5 KDC
+kdc_flags="" # Additional flags to the kerberos 5 KDC
+kdc_restart="NO" # Auto restart kdc on abnormal termination
+kdc_restart_delay="" # Auto restart delay seconds
+kadmind_enable="NO" # Run kadmind (or NO)
+kadmind_program="/usr/libexec/kadmind" # path to kadmind
+kpasswdd_enable="NO" # Run kpasswdd (or NO)
+kpasswdd_program="/usr/libexec/kpasswdd" # path to kpasswdd
+kfd_enable="NO" # Run kfd (or NO)
+kfd_program="/usr/libexec/kfd" # path to kerberos 5 kfd daemon
+kfd_flags=""
+ipropd_master_enable="NO" # Run Heimdal incremental propagation daemon
+ # (master daemon).
+ipropd_master_program="/usr/libexec/ipropd-master"
+ipropd_master_flags="" # Flags to ipropd-master.
+ipropd_master_keytab="/etc/krb5.keytab" # keytab for ipropd-master.
+ipropd_master_slaves="" # slave node names used for /var/heimdal/slaves.
+ipropd_slave_enable="NO" # Run Heimdal incremental propagation daemon
+ # (slave daemon).
+ipropd_slave_program="/usr/libexec/ipropd-slave"
+ipropd_slave_flags="" # Flags to ipropd-slave.
+ipropd_slave_keytab="/etc/krb5.keytab" # keytab for ipropd-slave.
+ipropd_slave_master="" # master node name.
+
+gssd_enable="NO" # Run the gssd daemon (or NO).
+gssd_program="/usr/sbin/gssd" # Path to gssd.
+gssd_flags="" # Flags for gssd.
+
+rwhod_enable="NO" # Run the rwho daemon (or NO).
+rwhod_flags="" # Flags for rwhod
+rarpd_enable="NO" # Run rarpd (or NO).
+rarpd_flags="-a" # Flags to rarpd.
+bootparamd_enable="NO" # Run bootparamd (or NO).
+bootparamd_flags="" # Flags to bootparamd
+pppoed_enable="NO" # Run the PPP over Ethernet daemon.
+pppoed_provider="*" # Provider and ppp(8) config file entry.
+pppoed_flags="-P /var/run/pppoed.pid" # Flags to pppoed (if enabled).
+pppoed_interface="em0" # The interface that pppoed runs on.
+sshd_enable="NO" # Enable sshd
+sshd_oomprotect="YES" # Don't kill sshd when swap space is exhausted.
+sshd_program="/usr/sbin/sshd" # path to sshd, if you want a different one.
+sshd_flags="" # Additional flags for sshd.
+
+### Network daemon (NFS): All need rpcbind_enable="YES" ###
+autofs_enable="NO" # Run autofs daemons.
+automount_flags="" # Flags to automount(8) (if autofs enabled).
+automountd_flags="" # Flags to automountd(8) (if autofs enabled).
+autounmountd_flags="" # Flags to autounmountd(8) (if autofs enabled).
+nfs_client_enable="NO" # This host is an NFS client (or NO).
+nfs_access_cache="60" # Client cache timeout in seconds
+nfs_server_enable="NO" # This host is an NFS server (or NO).
+nfs_server_flags="-u -t" # Flags to nfsd (if enabled).
+nfs_server_managegids="NO" # The NFS server maps gids for AUTH_SYS (or NO).
+nfs_server_maxio="131072" # Maximum I/O size for the nfsd.
+mountd_enable="NO" # Run mountd (or NO).
+mountd_flags="-r -S" # Flags to mountd (if NFS server enabled).
+weak_mountd_authentication="NO" # Allow non-root mount requests to be served.
+nfs_reserved_port_only="YES" # Provide NFS only on secure port (or NO).
+nfs_bufpackets="" # bufspace (in packets) for client
+rpc_lockd_enable="NO" # Run NFS rpc.lockd needed for client/server.
+rpc_lockd_flags="" # Flags to rpc.lockd (if enabled).
+rpc_statd_enable="NO" # Run NFS rpc.statd needed for client/server.
+rpc_statd_flags="" # Flags to rpc.statd (if enabled).
+rpcbind_enable="NO" # Run the portmapper service (YES/NO).
+rpcbind_program="/usr/sbin/rpcbind" # path to rpcbind, if you want a different one.
+rpcbind_flags="" # Flags to rpcbind (if enabled).
+rpc_ypupdated_enable="NO" # Run if NIS master and SecureRPC (or NO).
+nfsv4_server_enable="NO" # Enable support for NFSv4
+nfsv4_server_only="NO" # Set NFS server to NFSv4 only
+nfscbd_enable="NO" # NFSv4 client side callback daemon
+nfscbd_flags="" # Flags for nfscbd
+nfsuserd_enable="NO" # NFSv4 user/group name mapping daemon
+nfsuserd_flags="" # Flags for nfsuserd
+tlsclntd_enable="NO" # Run rpc.tlsclntd needed for NFS-over-TLS mount
+tlsclntd_flags="" # Flags for rpc.tlsclntd
+tlsservd_enable="NO" # Run rpc.tlsservd needed for NFS-over-TLS nfsd
+tlsservd_flags="" # Flags for rpc.tlsservd
+
+### Network Time Services options: ###
+ntpdate_enable="NO" # Run ntpdate to sync time on boot (or NO).
+ntpdate_program="/usr/sbin/ntpdate" # path to ntpdate, if you want a different one.
+ntpdate_flags="-b" # Flags to ntpdate (if enabled).
+ntpdate_config="/etc/ntp.conf" # ntpdate(8) configuration file
+ntpdate_hosts="" # Whitespace-separated list of ntpdate(8) servers.
+ntpd_enable="NO" # Run ntpd Network Time Protocol (or NO).
+ntpd_program="/usr/sbin/ntpd" # path to ntpd, if you want a different one.
+ntpd_config="/etc/ntp.conf" # ntpd(8) configuration file
+ntpd_sync_on_start="NO" # Sync time on ntpd startup, even if offset is high
+ntpd_flags="" # Additional flags to ntpd
+ntp_src_leapfile="/etc/ntp/leap-seconds"
+ # Initial source for ntpd leapfile
+ntp_db_leapfile="/var/db/ntpd.leap-seconds.list"
+ # Canonical place to get the leap seconds from
+ntp_leapfile_sources="https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list https://data.iana.org/time-zones/tzdb/leap-seconds.list"
+ # Source from which to fetch leapfile
+ntp_leapfile_fetch_opts="-mq" # Options to use for ntp leapfile fetch,
+ # e.g. --no-verify-peer
+ntp_leapfile_expiry_days=30 # Check for new leapfile 30 days prior to
+ # expiry.
+ntp_leapfile_fetch_verbose="NO" # Be verbose during NTP leapfile fetch
+
+# Network Information Services (NIS) options: All need rpcbind_enable="YES" ###
+nis_client_enable="NO" # We're an NIS client (or NO).
+nis_client_flags="" # Flags to ypbind (if enabled).
+nis_ypset_enable="NO" # Run ypset at boot time (or NO).
+nis_ypset_flags="" # Flags to ypset (if enabled).
+nis_server_enable="NO" # We're an NIS server (or NO).
+nis_server_flags="" # Flags to ypserv (if enabled).
+nis_ypxfrd_enable="NO" # Run rpc.ypxfrd at boot time (or NO).
+nis_ypxfrd_flags="" # Flags to rpc.ypxfrd (if enabled).
+nis_yppasswdd_enable="NO" # Run rpc.yppasswdd at boot time (or NO).
+nis_yppasswdd_flags="" # Flags to rpc.yppasswdd (if enabled).
+nis_ypldap_enable="NO" # Run ypldap at boot time (or NO).
+nis_ypldap_flags="" # Flags to ypldap (if enabled).
+
+### SNMP daemon ###
+# Be sure to understand the security implications of running SNMP v1/v2
+# in your network.
+bsnmpd_enable="NO" # Run the SNMP daemon (or NO).
+bsnmpd_flags="" # Flags for bsnmpd.
+
+### Network routing options: ###
+defaultrouter="NO" # Set to default gateway (or NO).
+#defaultrouter_fibN="192.0.2.1" # Use this form to set a gateway for FIB N
+static_arp_pairs="" # Set to static ARP list (or leave empty).
+static_ndp_pairs="" # Set to static NDP list (or leave empty).
+static_routes="" # Set to static route list (or leave empty).
+gateway_enable="NO" # Set to YES if this host will be a gateway.
+routed_enable="NO" # Set to YES to enable a routing daemon.
+routed_program="/sbin/routed" # Name of routing daemon to use if enabled.
+routed_flags="-q" # Flags for routing daemon.
+arpproxy_all="NO" # replaces obsolete kernel option ARP_PROXYALL.
+forward_sourceroute="NO" # do source routing (only if gateway_enable is set to "YES")
+accept_sourceroute="NO" # accept source routed packets to us
+
+### Bluetooth ###
+hcsecd_enable="NO" # Enable hcsecd(8) (or NO)
+hcsecd_config="/etc/bluetooth/hcsecd.conf" # hcsecd(8) configuration file
+
+sdpd_enable="NO" # Enable sdpd(8) (or NO)
+sdpd_control="/var/run/sdp" # sdpd(8) control socket
+sdpd_groupname="nobody" # set spdp(8) user/group to run as after
+sdpd_username="nobody" # it initializes
+
+bthidd_enable="NO" # Enable bthidd(8) (or NO)
+bthidd_config="/etc/bluetooth/bthidd.conf" # bthidd(8) configuration file
+bthidd_hids="/var/db/bthidd.hids" # bthidd(8) known HID devices file
+bthidd_evdev_support="AUTO" # AUTO depends on EVDEV_SUPPORT kernel option
+
+rfcomm_pppd_server_enable="NO" # Enable rfcomm_pppd(8) in server mode (or NO)
+rfcomm_pppd_server_profile="one two" # Profile to use from /etc/ppp/ppp.conf
+#
+#rfcomm_pppd_server_one_bdaddr="" # Override local bdaddr for 'one'
+rfcomm_pppd_server_one_channel="1" # Override local channel for 'one'
+#rfcomm_pppd_server_one_register_sp="NO" # Override SP and DUN register
+#rfcomm_pppd_server_one_register_dun="NO" # for 'one'
+#
+#rfcomm_pppd_server_two_bdaddr="" # Override local bdaddr for 'two'
+rfcomm_pppd_server_two_channel="3" # Override local channel for 'two'
+#rfcomm_pppd_server_two_register_sp="NO" # Override SP and DUN register
+#rfcomm_pppd_server_two_register_dun="NO" # for 'two'
+
+ubthidhci_enable="NO" # Switch an USB BT controller present on
+#ubthidhci_busnum="3" # bus 3 and addr 2 from HID mode to HCI mode.
+#ubthidhci_addr="2" # Check usbconfig list to find the correct
+ # numbers for your system.
+
+### Network link/usability verification options
+netwait_enable="NO" # Enable rc.d/netwait (or NO)
+#netwait_ip="" # Wait for ping response from any IP in this list.
+netwait_timeout="60" # Total number of seconds to perform pings.
+#netwait_if="" # Wait for active link on each intf in this list.
+netwait_if_timeout="30" # Total number of seconds to monitor link state.
+netwait_dad="NO" # Wait for DAD to complete
+netwait_dad_timeout="" # Total number of seconds to wait for DAD, zero
+ # or unset to autodetect
+
+### Miscellaneous network options: ###
+icmp_bmcastecho="NO" # respond to broadcast ping packets
+
+### IPv6 options: ###
+ipv6_network_interfaces="auto" # List of IPv6 network interfaces
+ # (or "auto" or "none").
+ipv6_activate_all_interfaces="NO" # If NO, interfaces which have no
+ # corresponding $ifconfig_IF_ipv6 is
+ # marked as IFDISABLED for security
+ # reason.
+ipv6_defaultrouter="NO" # Set to IPv6 default gateway (or NO).
+#ipv6_defaultrouter="2002:c058:6301::" # Use this for 6to4 (RFC 3068)
+#ipv6_defaultrouter_fibN="2001:db8::" # Use this form to set a gateway for FIB N
+ipv6_static_routes="" # Set to static route list (or leave empty).
+#ipv6_static_routes="xxx" # An example to set fec0:0000:0000:0006::/64
+ # route toward loopback interface.
+#ipv6_route_xxx="fec0:0000:0000:0006:: -prefixlen 64 ::1"
+ipv6_gateway_enable="NO" # Set to YES if this host will be a gateway.
+ipv6_cpe_wanif="NO" # Set to the upstream interface name if this
+ # node will work as a router to forward IPv6
+ # packets not explicitly addressed to itself.
+ipv6_privacy="NO" # Use privacy address on RA-receiving IFs
+ # (RFC 4941)
+
+route6d_enable="NO" # Set to YES to enable an IPv6 routing daemon.
+route6d_program="/usr/sbin/route6d" # Name of IPv6 routing daemon.
+route6d_flags="" # Flags to IPv6 routing daemon.
+#route6d_flags="-l" # Example for route6d with only IPv6 site local
+ # addrs.
+#route6d_flags="-q" # If you want to run a routing daemon on an end
+ # node, you should stop advertisement.
+#ipv6_network_interfaces="em0 em1" # Examples for router
+ # or static configuration for end node.
+ # Choose correct prefix value.
+#ipv6_prefix_em0="fec0:0000:0000:0001 fec0:0000:0000:0002" # Examples for rtr.
+#ipv6_prefix_em1="fec0:0000:0000:0003 fec0:0000:0000:0004" # Examples for rtr.
+ipv6_default_interface="NO" # Default output interface for scoped addrs.
+ # This works only with
+ # ipv6_gateway_enable="NO".
+rtsol_flags="-i" # Flags to IPv6 router solicitation.
+rtsold_enable="NO" # Set to YES to enable an IPv6 router
+ # solicitation daemon.
+rtsold_flags="-a -i" # Flags to an IPv6 router solicitation
+ # daemon.
+rtadvd_enable="NO" # Set to YES to enable an IPv6 router
+ # advertisement daemon. If set to YES,
+ # this router becomes a possible candidate
+ # IPv6 default router for local subnets.
+rtadvd_flags="" # Flags to the IPv6 router advertisement daemon.
+rtadvd_interfaces="" # Interfaces rtadvd sends RA packets.
+stf_interface_ipv4addr="" # Local IPv4 addr for 6to4 IPv6 over IPv4
+ # tunneling interface. Specify this entry
+ # to enable 6to4 interface.
+stf_interface_ipv4plen="0" # Prefix length for 6to4 IPv4 addr,
+ # to limit peer addr range. Effective value
+ # is 0-31.
+stf_interface_ipv6_ifid="0:0:0:1" # IPv6 interface id for stf0.
+ # If you like, you can set "AUTO" for this.
+stf_interface_ipv6_slaid="0000" # IPv6 Site Level Aggregator for stf0
+ipv6_ipv4mapping="NO" # Set to "YES" to enable IPv4 mapped IPv6 addr
+ # communication. (like ::ffff:a.b.c.d)
+ip6addrctl_enable="YES" # Set to YES to enable default address selection
+ip6addrctl_verbose="NO" # Set to YES to enable verbose configuration messages
+ip6addrctl_policy="AUTO" # A pre-defined address selection policy
+ # (ipv4_prefer, ipv6_prefer, or AUTO)
+
+##############################################################
+### System console options #################################
+##############################################################
+
+keyboard="" # keyboard device to use (default /dev/kbd0).
+keymap="NO" # keymap in /usr/share/{syscons,vt}/keymaps/* (or NO).
+keyrate="NO" # keyboard rate to: slow, normal, fast (or NO).
+keybell="NO" # See kbdcontrol(1) for options. Use "off" to disable.
+keychange="NO" # function keys default values (or NO).
+cursor="NO" # cursor type {normal|blink|destructive} (or NO).
+scrnmap="NO" # screen map in /usr/share/syscons/scrnmaps/* (or NO).
+font8x16="NO" # font 8x16 from /usr/share/{syscons,vt}/fonts/* (or NO).
+font8x14="NO" # font 8x14 from /usr/share/{syscons,vt}/fonts/* (or NO).
+font8x8="NO" # font 8x8 from /usr/share/{syscons,vt}/fonts/* (or NO).
+blanktime="300" # blank time (in seconds) or "NO" to turn it off.
+saver="NO" # screen saver: Uses /boot/kernel/${saver}_saver.ko
+moused_nondefault_enable="YES" # Treat non-default mice as enabled unless
+ # specifically overridden in rc.conf(5).
+moused_enable="NO" # Run the mouse daemon.
+moused_type="evdev" # See man page for rc.conf(5) for available settings.
+moused_port="/dev/psm0" # Set to your mouse port.
+moused_flags="" # Any additional flags to moused.
+mousechar_start="NO" # if 0xd0-0xd3 default range is occupied in your
+ # language code table, specify alternative range
+ # start like mousechar_start=3, see vidcontrol(1)
+msconvd_enable="NO" # Run the mouse protocol conversion daemon.
+msconvd_type="auto" # See rc.conf(5) man page for available moused_type-s.
+msconvd_ports="" # List of msconvd ports.
+msconvd_flags="" # Any additional flags to msconvd.
+allscreens_flags="" # Set this vidcontrol mode for all virtual screens
+allscreens_kbdflags="" # Set this kbdcontrol mode for all virtual screens
+
+##############################################################
+### Mail Transfer Agent (MTA) options ######################
+##############################################################
+
+# Settings for /etc/rc.d/sendmail:
+sendmail_enable="NONE" # Run the sendmail inbound daemon (YES/NO/NONE).
+ # If NONE, don't start any sendmail processes.
+sendmail_pidfile="/var/run/sendmail.pid" # sendmail pid file
+sendmail_procname="/usr/sbin/sendmail" # sendmail process name
+sendmail_flags="-L sm-mta -bd -q30m" # Flags to sendmail (as a server)
+sendmail_cert_create="YES" # Create a server certificate if none (YES/NO)
+#sendmail_cert_cn="CN" # CN of the generate certificate
+sendmail_submit_enable="YES" # Start a localhost-only MTA for mail submission
+sendmail_submit_flags="-L sm-mta -bd -q30m -ODaemonPortOptions=Addr=localhost"
+ # Flags for localhost-only MTA
+sendmail_outbound_enable="YES" # Dequeue stuck mail (YES/NO).
+sendmail_outbound_flags="-L sm-queue -q30m" # Flags to sendmail (outbound only)
+sendmail_msp_queue_enable="YES" # Dequeue stuck clientmqueue mail (YES/NO).
+sendmail_msp_queue_flags="-L sm-msp-queue -Ac -q30m"
+ # Flags for sendmail_msp_queue daemon.
+sendmail_rebuild_aliases="NO" # Run newaliases if necessary (YES/NO).
+
+
+##############################################################
+### Miscellaneous administrative options ###################
+##############################################################
+
+auditd_enable="NO" # Run the audit daemon.
+auditd_program="/usr/sbin/auditd" # Path to the audit daemon.
+auditd_flags="" # Which options to pass to the audit daemon.
+auditdistd_enable="NO" # Run the audit daemon.
+auditdistd_program="/usr/sbin/auditdistd" # Path to the auditdistd daemon.
+auditdistd_flags="" # Which options to pass to the auditdistd daemon.
+cron_enable="YES" # Run the periodic job daemon.
+cron_program="/usr/sbin/cron" # Which cron executable to run (if enabled).
+cron_dst="YES" # Handle DST transitions intelligently (YES/NO)
+cron_flags="" # Which options to pass to the cron daemon.
+cfumass_enable="NO" # Create default LUN for cfumass(4).
+cfumass_dir="/var/cfumass" # File to LUN's contents.
+cfumass_image="/var/tmp/cfumass.img" # LUN's backing file path.
+lpd_enable="NO" # Run the line printer daemon.
+lpd_program="/usr/sbin/lpd" # path to lpd, if you want a different one.
+lpd_flags="" # Flags to lpd (if enabled).
+nscd_enable="NO" # Run the nsswitch caching daemon.
+chkprintcap_enable="NO" # Run chkprintcap(8) before running lpd.
+chkprintcap_flags="-d" # Create missing directories by default.
+dumpdev="AUTO" # Device to crashdump to (device name, AUTO, or NO);
+ # this should be commented out here
+ # for stable branches to respect kenv.
+dumpon_flags="" # Options to pass to dumpon(8), followed by dumpdev.
+dumpdir="/var/crash" # Directory where crash dumps are to be stored
+savecore_enable="YES" # Extract core from dump devices if any
+savecore_flags="-m 10" # Used if dumpdev is enabled above, and present.
+ # By default, only the 10 most recent kernel dumps
+ # are saved.
+service_delete_empty="NO" # Have 'service delete' remove empty rc.conf.d files.
+crashinfo_enable="YES" # Automatically generate crash dump summary.
+crashinfo_program="/usr/sbin/crashinfo" # Script to generate crash dump summary.
+quota_enable="NO" # turn on quotas on startup (or NO).
+check_quotas="YES" # Check quotas on startup (or NO).
+quotaon_flags="-a" # Turn quotas on for all file systems (if enabled)
+quotaoff_flags="-a" # Turn quotas off for all file systems at shutdown
+quotacheck_flags="-a" # Check all file system quotas (if enabled)
+accounting_enable="NO" # Turn on process accounting (or NO).
+firstboot_sentinel="/firstboot" # Scripts with "firstboot" keyword are run if
+ # this file exists. Should be on a R/W filesystem so
+ # the file can be deleted after the boot completes.
+sysvipc_enable="NO" # Load System V IPC primitives at startup (or NO).
+linux_enable="NO" # Linux binary compatibility loaded at startup (or NO).
+linux_mounts_enable="YES" # If linux_enable is set to YES, mount Linux-specific
+ # filesystems at startup.
+clear_tmp_enable="NO" # Clear /tmp at startup.
+clear_tmp_X="YES" # Clear and recreate X11-related directories in /tmp
+ldconfig_insecure="NO" # Set to YES to disable ldconfig security checks
+ldconfig_paths="/usr/lib/compat ${_localbase}/lib ${_localbase}/lib/compat/pkg"
+ # shared library search paths
+ldconfig32_paths="/usr/lib32/compat"
+ # 32-bit compatibility shared library search paths
+ldconfig_local_dirs="${_localbase}/libdata/ldconfig"
+ # Local directories with ldconfig configuration files.
+ldconfig_local32_dirs="${_localbase}/libdata/ldconfig32"
+ # Local directories with 32-bit compatibility ldconfig
+ # configuration files.
+kern_securelevel_enable="NO" # kernel security level (see security(7))
+kern_securelevel="-1" # range: -1..3 ; `-1' is the most insecure
+ # Note that setting securelevel to 0 will result
+ # in the system booting with securelevel set to 1, as
+ # init(8) will raise the level when rc(8) completes.
+update_motd="YES" # update version info in /var/run/motd (or NO)
+entropy_boot_file="/boot/entropy" # Set to NO to disable very early
+ # (used at early boot time) entropy caching through reboots.
+entropy_file="/entropy" # Set to NO to disable late (used when going multi-user)
+ # entropy through reboots.
+ # /var/db/entropy-file is preferred if / is not avail.
+entropy_dir="/var/db/entropy" # Set to NO to disable caching entropy via cron.
+entropy_save_sz="4096" # Size of the entropy cache files.
+entropy_save_num="8" # Number of entropy cache files to save.
+harvest_mask="4607" # Entropy device harvests all but the very invasive sources.
+ # (See 'sysctl kern.random.harvest' and random(4))
+osrelease_enable="YES" # Update /var/run/os-release on boot (or NO).
+osrelease_file="/var/run/os-release" # File to update for os-release.
+osrelease_perms="444" # Default permission for os-release file.
+dmesg_enable="YES" # Save dmesg(8) to /var/run/dmesg.boot
+dmesg_umask="022" # Default umask for /var/run/dmesg.boot file.
+watchdogd_enable="NO" # Start the software watchdog daemon
+watchdogd_flags="" # Flags to watchdogd (if enabled)
+watchdogd_timeout="" # watchdogd timeout, overrides -t in watchdogd_flags
+watchdogd_shutdown_timeout="" # Timeout to use after watchdogd is stopped.
+ # Has effect only for system shutdown.
+ # Overrides -x in watchdogd_flags.
+devfs_rulesets="/etc/defaults/devfs.rules /etc/devfs.rules" # Files containing
+ # devfs(8) rules.
+devfs_system_ruleset="" # The name (NOT number) of a ruleset to apply to /dev
+devfs_set_rulesets="" # A list of /mount/dev=ruleset_name settings to
+ # apply (must be mounted already, i.e. fstab(5))
+devfs_load_rulesets="YES" # Enable to always load the default rulesets
+performance_cx_lowest="NONE" # Online CPU idle state
+performance_cpu_freq="NONE" # Online CPU frequency
+economy_cx_lowest="Cmax" # Offline CPU idle state
+economy_cpu_freq="NONE" # Offline CPU frequency
+virecover_enable="YES" # Perform housekeeping for the vi(1) editor
+ugidfw_enable="NO" # Load mac_bsdextended(4) rules on boot
+bsdextended_script="/etc/rc.bsdextended" # Default mac_bsdextended(4)
+ # ruleset file.
+newsyslog_enable="YES" # Run newsyslog at startup.
+newsyslog_flags="-CN" # Newsyslog flags to create marked files
+mixer_enable="YES" # Run the sound mixer.
+opensm_enable="NO" # Opensm(8) for infiniband devices defaults to off
+nuageinit_enable="NO" # Run nuageinit at startup
+
+# rctl(8) requires kernel options RACCT and RCTL
+rctl_enable="YES" # Load rctl(8) rules on boot
+rctl_rules="/etc/rctl.conf" # rctl(8) ruleset. See rctl.conf(5).
+
+iovctl_files="" # Config files for iovctl(8)
+
+##############################################################
+### Jail Configuration (see rc.conf(5) manual page) ##########
+##############################################################
+jail_enable="NO" # Set to NO to disable starting of any jails
+jail_conf="/etc/jail.conf" # Configuration file for jail(8)
+jail_confwarn="YES" # Prevent warning about obsolete per-jail configuration
+jail_parallel_start="NO" # Start jails in the background
+jail_list="" # Space separated list of names of jails
+jail_reverse_stop="NO" # Stop jails in reverse order
+
+##############################################################
+### Define source_rc_confs, the mechanism used by /etc/rc.* ##
+### scripts to source rc_conf_files overrides safely. ##
+##############################################################
+
+if [ -z "${source_rc_confs_defined}" ]; then
+ source_rc_confs_defined=yes
+ source_rc_confs() {
+ local i sourced_files
+ for i in ${rc_conf_files}; do
+ case ${sourced_files} in
+ *:$i:*)
+ ;;
+ *)
+ sourced_files="${sourced_files}:$i:"
+ if [ -r $i ]; then
+ . $i
+ fi
+ ;;
+ esac
+ done
+ # Re-do process to pick up [possibly] redefined $rc_conf_files
+ for i in ${rc_conf_files}; do
+ case ${sourced_files} in
+ *:$i:*)
+ ;;
+ *)
+ sourced_files="${sourced_files}:$i:"
+ if [ -r $i ]; then
+ . $i
+ fi
+ ;;
+ esac
+ done
+ }
+fi
+
+# Allow vendors to override FreeBSD defaults in /etc/default/rc.conf
+# without the need to carefully manage /etc/rc.conf.
+if [ -r /etc/defaults/vendor.conf ]; then
+ . /etc/defaults/vendor.conf
+fi
diff --git a/libexec/rc/rc.d/DAEMON b/libexec/rc/rc.d/DAEMON
new file mode 100755
index 000000000000..f31fddb55d7e
--- /dev/null
+++ b/libexec/rc/rc.d/DAEMON
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: DAEMON
+# REQUIRE: NETWORKING SERVERS
+
+# This is a dummy dependency, to ensure that general purpose daemons
+# are run _after_ the above are.
diff --git a/libexec/rc/rc.d/FILESYSTEMS b/libexec/rc/rc.d/FILESYSTEMS
new file mode 100755
index 000000000000..1bf52077be8e
--- /dev/null
+++ b/libexec/rc/rc.d/FILESYSTEMS
@@ -0,0 +1,11 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: FILESYSTEMS
+# REQUIRE: root mountcritlocal cleanvar tmp
+
+# This is a dummy dependency, for services which require filesystems
+# to be mounted before starting. It also serves as the default early /
+# late divider; after this point, rc.d directories are rescanned to
+# catch scripts from other filesystems than /.
diff --git a/libexec/rc/rc.d/LOGIN b/libexec/rc/rc.d/LOGIN
new file mode 100755
index 000000000000..7f95980e11ec
--- /dev/null
+++ b/libexec/rc/rc.d/LOGIN
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: LOGIN
+# REQUIRE: DAEMON
+
+# This is a dummy dependency to ensure user services such as xdm,
+# inetd, cron and kerberos are started after everything else, in case
+# the administrator has increased the system security level and
+# wants to delay user logins until the system is (almost) fully
+# operational.
diff --git a/libexec/rc/rc.d/Makefile b/libexec/rc/rc.d/Makefile
new file mode 100644
index 000000000000..3b7f45e8f101
--- /dev/null
+++ b/libexec/rc/rc.d/Makefile
@@ -0,0 +1,384 @@
+.include <src.opts.mk>
+
+CONFDIR= /etc/rc.d
+CONFGROUPS= CONFS
+CONFSPACKAGE= rc
+
+# Files which are always installed and go in the -rc package.
+CONFS= DAEMON \
+ FILESYSTEMS \
+ LOGIN \
+ NETWORKING \
+ SERVERS \
+ adjkerntz \
+ bgfsck \
+ bridge \
+ cfumass \
+ cleanvar \
+ cleartmp \
+ ddb \
+ defaultroute \
+ devfs \
+ dmesg \
+ dumpon \
+ fsck \
+ growfs \
+ growfs_fstab \
+ hostid \
+ hostid_save \
+ hostname \
+ iovctl \
+ ip6addrctl \
+ ipsec \
+ kld \
+ kldxref \
+ ldconfig \
+ linux \
+ local \
+ localpkg \
+ mixer \
+ motd \
+ mountcritlocal \
+ mountcritremote \
+ mountlate \
+ mdconfig \
+ mdconfig2 \
+ msgs \
+ netif \
+ netoptions \
+ netwait \
+ noshutdown \
+ os-release \
+ pwcheck \
+ quota \
+ random \
+ rarpd \
+ rctl \
+ root \
+ routing \
+ rpcbind \
+ rtadvd \
+ rtsold \
+ savecore \
+ securelevel \
+ serial \
+ static_arp \
+ static_ndp \
+ stf \
+ swap \
+ swaplate \
+ sysctl \
+ sysctl_lastload \
+ sysvipc \
+ tmp \
+ ugidfw \
+ var \
+ var_run \
+ watchdogd
+
+# Groups for files which don't go in -rc, or which depend on src.conf knobs.
+
+.if ${MK_ACCT} != "no" || ${MK_UTMPX} != "no"
+CONFGROUPS+= ACCT
+ACCTPACKAGE= acct
+.if ${MK_ACCT} != "no"
+ACCT= accounting
+.endif
+.if ${MK_UTMPX} != "no"
+ACCT+= utx
+.endif
+.endif
+
+CONFGROUPS.${MK_ACPI}+= ACPI
+ACPIPACKAGE= acpi
+ACPI= power_profile
+
+CONFGROUPS.${MK_APM}+= APM
+APMPACKAGE= apm
+APM= apm
+.if ${MACHINE} == "i386"
+APM+= apmd
+.endif
+
+CONFGROUPS.${MK_AUDIT}+= AUDIT
+AUDITPACKAGE= audit
+AUDIT= auditd \
+ auditdistd
+
+CONFGROUPS.${MK_AUTOFS}+= AUTOFS
+AUTOFSPACKAGE= autofs
+AUTOFS= automount \
+ automountd \
+ autounmountd
+
+CONFGROUPS.${MK_BLOCKLIST}+= BLOCKLIST
+BLOCKLISTPACKAGE= blocklist
+BLOCKLIST= blacklistd \
+ blocklistd
+
+CONFGROUPS.${MK_BLUETOOTH}+= BLUETOOTH
+BLUETOOTHPACKAGE= bluetooth
+BLUETOOTH= bluetooth \
+ bthidd \
+ hcsecd \
+ rfcomm_pppd_server \
+ sdpd \
+ ubthidhci
+
+CONFGROUPS.${MK_BOOTPARAMD}+= BOOTPARAMD
+BOOTPARAMD= bootparams
+
+CONFGROUPS.${MK_BSNMP}+= BSNMP
+BSNMPPACKAGE= bsnmp
+BSNMP= bsnmpd
+
+CONFGROUPS.${MK_CCD}+= CCD
+CCDPACKAGE= ccdconfig
+CCD= ccd
+
+CONFGROUPS+= DEVD
+DEVDPACKAGE= devd
+DEVD= devd
+
+CONFGROUPS+= DEVMATCH
+DEVMATCHPACKAGE= devmatch
+DEVMATCH= devmatch
+
+CONFGROUPS+= DHCLIENT
+DHCLIENTPACKAGE= dhclient
+DHCLIENT= dhclient
+
+CONFGROUPS+= CRON
+CRONPACKAGE= cron
+CRON= cron
+
+CONFGROUPS+= CTL
+CTLPACKAGE= ctl
+CTL= ctld
+
+CONFGROUPS+= GEOM
+GEOMPACKAGE= geom
+GEOM= geli \
+ geli2 \
+ gptboot
+
+CONFGROUPS+= GGATED
+GGATEDPACKAGE= ggate
+GGATED= ggated
+
+CONFGROUPS.${MK_KERBEROS_SUPPORT}+=GSSD
+GSSDPACKAGE= gssd
+GSSD= gssd
+
+CONFGROUPS.${MK_HAST}+= HAST
+HASTPACKAGE= hast
+HAST= hastd
+
+CONFGROUPS.${MK_INETD}+= INETD
+INETDPACKAGE= inetd
+INETD= inetd
+
+CONFGROUPS.${MK_IPFILTER}+= IPF
+IPFPACKAGE= ipf
+IPF= ipfilter \
+ ipfs \
+ ipmon \
+ ipnat \
+ ippool
+
+CONFGROUPS.${MK_IPFW}+= IPFW
+IPFWPACKAGE= ipfw
+IPFW= ipfw \
+ dnctl
+.if ${MK_NETGRAPH} != "no"
+IPFW+= ipfw_netflow
+.endif
+
+CONFGROUPS.${MK_ISCSI}+= ISCSI
+ISCSIPACKAGE= iscsi
+ISCSI= iscsictl \
+ iscsid
+
+# natd is only built when ipfw is built
+CONFGROUPS.${MK_IPFW}+= NATD
+NATDPACKAGE= natd
+NATD= natd
+
+CONFGROUPS.${MK_JAIL}+= JAIL
+JAILPACKAGE= jail
+JAIL= jail
+
+CONFGROUPS.${MK_LPR}+= LP
+LPPACKAGE= lp
+LP= lpd
+
+CONFGROUPS+= NEWSYSLOG
+NEWSYSLOGPACKAGE= newsyslog
+NEWSYSLOG= newsyslog
+
+CONFGROUPS+= NFS
+NFSPACKAGE= nfs
+NFS= lockd \
+ mountd \
+ nfscbd \
+ nfsclient \
+ nfsd \
+ nfsuserd \
+ statd
+
+CONFGROUPS.${MK_NIS}+= NIS
+NISPACKAGE= yp
+NIS= ypbind \
+ ypldap \
+ yppasswdd \
+ ypserv \
+ ypset \
+ ypupdated \
+ ypxfrd \
+ nisdomain
+
+CONFGROUPS.${MK_NS_CACHING}+= NSCD
+NSCD= nscd
+
+CONFGROUPS.${MK_NTP}+= NTP
+NTPPACKAGE= ntp
+NTP= ntpd \
+ ntpdate
+
+CONFGROUPS.${MK_NUAGEINIT}+= NUAGEINIT
+NUAGEINITPACKAGE= nuageinit
+NUAGEINIT= nuageinit \
+ nuageinit_post_net \
+ nuageinit_user_data_script
+
+CONFGROUPS.${MK_OFED_EXTRA}+= OPENSM
+OPENSM= opensm
+
+CONFGROUPS.${MK_PF}+= PF
+PFPACKAGE= pf
+PF= pf \
+ pflog \
+ pfsync \
+ ftp-proxy
+
+CONFGROUPS+= POWERD
+POWERDPACKAGE= powerd
+POWERD= powerd
+
+CONFGROUPS.${MK_PPP}+= PPP
+PPPPACKAGE= ppp
+PPP= ppp
+
+CONFGROUPS+= PPPOED
+PPPOEDPACKAGE= ppp
+PPPOED= pppoed
+
+CONFGROUPS+= SYSLOGD
+SYSLOGDPACKAGE= syslogd
+SYSLOGD= syslogd
+
+CONFGROUPS+= RCMDS
+RCMDSPACKAGE= rcmds
+RCMDS= rwho
+
+CONFGROUPS+= RESOLVCONF
+RESOLVCONFPACKAGE= resolvconf
+RESOLVCONF= resolv
+
+CONFGROUPS.${MK_SENDMAIL}+= SENDMAIL
+SENDMAILPACKAGE= sendmail
+SENDMAIL= sendmail
+
+CONFGROUPS.${MK_OPENSSH}+= SSH
+SSHPACKAGE= ssh
+SSH= sshd
+
+CONFGROUPS.${MK_UNBOUND}+= UNBOUND
+UNBOUNDPACKAGE= local-unbound
+UNBOUND= local_unbound
+
+CONFGROUPS.${MK_VI}+= VI
+VIPACKAGE= vi
+VI= virecover
+
+CONFGROUPS.${MK_CUSE}+= VOSS
+VOSSPACKAGE= sound
+VOSS= virtual_oss
+
+CONFGROUPS.${MK_WIRELESS}+= HOSTAPD
+HOSTAPDPACKAGE= hostapd
+HOSTAPD= hostapd
+
+CONFGROUPS.${MK_WIRELESS}+= WPA
+WPAPACKAGE= wpa
+WPA= wpa_supplicant
+
+CONFGROUPS.${MK_ZFS}+= ZFS
+ZFSPACKAGE= zfs
+ZFS= zfs \
+ zfsbe \
+ zfsd \
+ zfskeys \
+ zpool \
+ zpoolreguid \
+ zpoolupgrade \
+ zvol
+
+CONFGROUPS.${MK_LEGACY_CONSOLE}+=SYSCONS
+SYSCONSPACKAGE= console-tools
+SYSCONS= moused \
+ msconvd \
+ syscons
+
+.if ${MK_KERBEROS} != "no"
+.if ${MK_MITKRB5} == "no"
+
+# Heimdal rc scripts
+CONFGROUPS+= HEIMDAL
+HEIMDAL= ipropd_master \
+ ipropd_slave \
+ kadmind \
+ kdc \
+ kfd \
+ kpasswdd
+HEIMDALPACKAGE= kerberos
+
+DIRS+= VAR_HEMIDAL
+VAR_HEMIDAL= /var/heimdal
+VAR_HEMIDAL_MODE= 700
+
+.else # ${MK_MITKRB5} != "no"
+
+# MIT KRB5 rc scripts
+CONFGROUPS+= KRB5
+KRB5= kadmind \
+ kdc
+KRB5PACKAGE= kerberos-kdc
+
+.endif # ${MK_MITKRB5}
+.endif # ${MK_KERBEROS}
+
+.if ${MK_OPENSSL} != "no" && ${MK_OPENSSL_KTLS} != "no"
+CONFGROUPS+= KTLS
+KTLS= tlsclntd \
+ tlsservd
+.endif
+
+.if ${MK_INET6} != "no" || ${MK_ROUTED} != "no"
+CONFGROUPS+= RIP
+RIPPACKAGE= rip
+
+.if ${MK_INET6} != "no"
+RIP+= route6d
+.endif
+.if ${MK_ROUTED} != "no"
+RIP+= routed
+.endif
+.endif
+
+.for fg in ${CONFGROUPS} ${CONFGROUPS.yes}
+${fg}MODE?= ${BINMODE}
+${fg}PACKAGE?= rc
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/libexec/rc/rc.d/NETWORKING b/libexec/rc/rc.d/NETWORKING
new file mode 100755
index 000000000000..402e20927a4c
--- /dev/null
+++ b/libexec/rc/rc.d/NETWORKING
@@ -0,0 +1,11 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: NETWORKING NETWORK
+# REQUIRE: netif netwait netoptions routing ppp ipfw stf
+# REQUIRE: defaultroute route6d resolv bridge
+# REQUIRE: static_arp static_ndp
+
+# This is a dummy dependency, for services which require networking
+# to be operational before starting.
diff --git a/libexec/rc/rc.d/SERVERS b/libexec/rc/rc.d/SERVERS
new file mode 100755
index 000000000000..0bcaec08c3e6
--- /dev/null
+++ b/libexec/rc/rc.d/SERVERS
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: SERVERS
+# REQUIRE: mountcritremote sysvipc linux ldconfig savecore watchdogd
+
+# This is a dummy dependency, for early-start servers relying on
+# some basic configuration.
diff --git a/libexec/rc/rc.d/accounting b/libexec/rc/rc.d/accounting
new file mode 100755
index 000000000000..1e0ece84fb15
--- /dev/null
+++ b/libexec/rc/rc.d/accounting
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: accounting
+# REQUIRE: mountcritremote
+# BEFORE: DAEMON
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="accounting"
+rcvar="accounting_enable"
+accounting_command="/usr/sbin/accton"
+accounting_file="/var/account/acct"
+
+extra_commands="rotate_log"
+
+start_cmd="accounting_start"
+stop_cmd="accounting_stop"
+rotate_log_cmd="accounting_rotate_log"
+
+create_accounting_file()
+{
+ install -o root -g wheel -m 0640 /dev/null "${accounting_file}"
+}
+
+accounting_start()
+{
+ local _dir
+
+ _dir="${accounting_file%/*}"
+ if [ ! -d "$_dir" ]; then
+ if ! mkdir -p -m 0750 "$_dir"; then
+ err 1 "Could not create $_dir."
+ fi
+ fi
+
+ if [ ! -e "$accounting_file" ]; then
+ echo -n "Creating accounting file ${accounting_file}"
+ create_accounting_file
+ echo '.'
+ fi
+
+ echo "Turning on accounting."
+ ${accounting_command} ${accounting_file}
+}
+
+accounting_stop()
+{
+ echo "Turning off accounting."
+ ${accounting_command}
+}
+
+accounting_rotate_log()
+{
+ # Note that this function must handle being called as "onerotate_log"
+ # (by the periodic scripts) when accounting is disabled, and handle
+ # being called multiple times (by an admin making mistakes) without
+ # anything having actually rotated the old .0 file out of the way.
+
+ if [ -e "${accounting_file}.0" ]; then
+ err 1 "Cannot rotate accounting log, ${accounting_file}.0 already exists."
+ fi
+
+ if [ ! -e "${accounting_file}" ]; then
+ err 1 "Cannot rotate accounting log, ${accounting_file} does not exist."
+ fi
+
+ mv ${accounting_file} ${accounting_file}.0
+
+ if checkyesno accounting_enable; then
+ create_accounting_file
+ ${accounting_command} "${accounting_file}"
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: jail can't manipulate accounting
+accounting_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/adjkerntz b/libexec/rc/rc.d/adjkerntz
new file mode 100755
index 000000000000..339f8add7201
--- /dev/null
+++ b/libexec/rc/rc.d/adjkerntz
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: adjkerntz
+# REQUIRE: FILESYSTEMS
+# BEFORE: netif
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="adjkerntz"
+start_cmd="adjkerntz -i"
+stop_cmd=":"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: jail can't modify kerntz
+adjkerntz_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/apm b/libexec/rc/rc.d/apm
new file mode 100755
index 000000000000..3187f41c3a50
--- /dev/null
+++ b/libexec/rc/rc.d/apm
@@ -0,0 +1,50 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: apm
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="apm"
+desc="Advanced power management"
+rcvar="apm_enable"
+start_precmd="apm_precmd"
+command="/usr/sbin/${name}"
+start_cmd="${command} -e enable"
+stop_cmd="${command} -e disable"
+status_cmd="apm_status"
+
+apm_precmd()
+{
+ case `${SYSCTL_N} hw.machine_arch` in
+ i386)
+ return 0
+ ;;
+ esac
+ return 1
+}
+
+apm_status()
+{
+ case `${command} -s` in
+ 1)
+ echo "APM is enabled."
+ return 0
+ ;;
+ 0)
+ echo "APM is disabled"
+ ;;
+ esac
+ return 1
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+apm_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/apmd b/libexec/rc/rc.d/apmd
new file mode 100755
index 000000000000..aeb5042342d6
--- /dev/null
+++ b/libexec/rc/rc.d/apmd
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: apmd
+# REQUIRE: DAEMON apm
+# BEFORE: LOGIN
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="apmd"
+desc="Advanced power management daemon"
+rcvar="apmd_enable"
+command="/usr/sbin/${name}"
+start_precmd="apmd_prestart"
+
+apmd_prestart()
+{
+ case `${SYSCTL_N} hw.machine_arch` in
+ i386)
+ force_depend apm || return 1
+
+ # Warn user about acpi apm compatibility support which
+ # does not work with apmd.
+ if [ ! -e /dev/apmctl ]; then
+ warn "/dev/apmctl not found; kernel is missing apm(4)"
+ fi
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+apmd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/auditd b/libexec/rc/rc.d/auditd
new file mode 100755
index 000000000000..caea2587a2e9
--- /dev/null
+++ b/libexec/rc/rc.d/auditd
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+#
+# Start up for the Audit daemon.
+#
+
+# PROVIDE: auditd
+# REQUIRE: syslogd
+# BEFORE: DAEMON
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="auditd"
+desc="Audit daemon"
+stop_cmd="auditd_stop"
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+rcvar="auditd_enable"
+command_args="${auditd_flags}"
+required_files="/etc/security/audit_class /etc/security/audit_control
+ /etc/security/audit_event /etc/security/audit_user
+ /etc/security/audit_warn"
+
+auditd_stop()
+{
+
+ /usr/sbin/audit -t
+ if [ -n "$rc_pid" ]; then
+ wait_for_pids $rc_pid
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+auditd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/auditdistd b/libexec/rc/rc.d/auditdistd
new file mode 100755
index 000000000000..0814c2a4d2c7
--- /dev/null
+++ b/libexec/rc/rc.d/auditdistd
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: auditdistd
+# REQUIRE: auditd
+# BEFORE: DAEMON
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="auditdistd"
+desc="Audit trail files distribution daemon"
+rcvar="${name}_enable"
+pidfile="/var/run/${name}.pid"
+command="/usr/sbin/${name}"
+required_files="/etc/security/${name}.conf"
+extra_commands="reload"
+
+: ${auditdistd_svcj_options:="net_basic"}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/automount b/libexec/rc/rc.d/automount
new file mode 100755
index 000000000000..19f367837189
--- /dev/null
+++ b/libexec/rc/rc.d/automount
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: automount
+# REQUIRE: nfsclient automountd
+# BEFORE: DAEMON
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="automount"
+rcvar="autofs_enable"
+start_cmd="automount_start"
+stop_cmd="automount_stop"
+required_modules="autofs"
+
+automount_start()
+{
+
+ /usr/sbin/automount ${automount_flags}
+}
+
+automount_stop()
+{
+
+ /sbin/umount -At autofs
+}
+
+load_rc_config $name
+
+# mounting shall not be performed in a svcj
+automount_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/automountd b/libexec/rc/rc.d/automountd
new file mode 100755
index 000000000000..b809e9dfc8ad
--- /dev/null
+++ b/libexec/rc/rc.d/automountd
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: automountd
+# REQUIRE: rpcbind ypset nfsclient FILESYSTEMS ldconfig
+# BEFORE: DAEMON
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="automountd"
+desc="daemon handling autofs mount requests"
+rcvar="autofs_enable"
+pidfile="/var/run/${name}.pid"
+command="/usr/sbin/${name}"
+required_modules="autofs"
+
+load_rc_config $name
+
+# mounting shall not be performed in a svcj
+automountd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/autounmountd b/libexec/rc/rc.d/autounmountd
new file mode 100755
index 000000000000..1d8b3bfa354f
--- /dev/null
+++ b/libexec/rc/rc.d/autounmountd
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: autounmountd
+# REQUIRE: FILESYSTEMS
+# BEFORE: DAEMON
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="autounmountd"
+desc="daemon unmounting automounted filesystems"
+rcvar="autofs_enable"
+pidfile="/var/run/${name}.pid"
+command="/usr/sbin/${name}"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+autounmountd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/bgfsck b/libexec/rc/rc.d/bgfsck
new file mode 100755
index 000000000000..dd5c330c3d11
--- /dev/null
+++ b/libexec/rc/rc.d/bgfsck
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: bgfsck
+# REQUIRE: cron devfs syslogd
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="background_fsck"
+desc="Run fsck in background"
+rcvar="background_fsck"
+start_cmd="bgfsck_start"
+start_precmd="bgfsck_start_precmd"
+stop_cmd=":"
+
+bgfsck_start_precmd()
+{
+ if [ $($ID -u) != 0 ]; then
+ err 1 "Must be root."
+ fi
+}
+
+bgfsck_start()
+{
+ : ${background_fsck_delay=0}
+ if [ -n "${rc_force}" ]; then
+ background_fsck_delay=0
+ fi
+ if [ ${background_fsck_delay} -lt 0 ]; then
+ warn "Background file system checks delayed indefinitely"
+ return 0
+ fi
+
+ bgfsck_msg='Starting background file system checks'
+ if [ "${background_fsck_delay}" -gt 0 ]; then
+ bgfsck_msg="${bgfsck_msg} in ${background_fsck_delay} seconds"
+ fi
+ if [ -z "${rc_force}" ]; then
+ startmsg "${bgfsck_msg}."
+ fi
+
+ (sleep ${background_fsck_delay}; nice -4 fsck -B -p) 2>&1 | \
+ logger -p daemon.notice -t fsck &
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+bgfsck_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/blacklistd b/libexec/rc/rc.d/blacklistd
new file mode 100755
index 000000000000..175e3e8c56b3
--- /dev/null
+++ b/libexec/rc/rc.d/blacklistd
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 The FreeBSD Foundation
+#
+# This software was developed by Kurt Lidl under sponsorship from the
+# FreeBSD Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: blacklistd
+# REQUIRE: netif ipfilter ipfw pf
+
+. /etc/rc.subr
+
+name="blacklistd"
+desc="The blacklist daemon has been renamed to blocklist"
+rcvar="blacklistd_enable"
+command="/usr/sbin/${name}"
+required_files="/etc/blacklistd.conf"
+start_precmd="blacklistd_prestart"
+
+# no svcj options needed
+: ${blacklistd_svcj_options:=""}
+
+blacklistd_prestart()
+{
+ echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+ echo "@ WARNING: blacklistd has been renamed to blocklistd @"
+ echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/blocklistd b/libexec/rc/rc.d/blocklistd
new file mode 100755
index 000000000000..f979162ec3e0
--- /dev/null
+++ b/libexec/rc/rc.d/blocklistd
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 The FreeBSD Foundation
+#
+# This software was developed by Kurt Lidl under sponsorship from the
+# FreeBSD Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: blocklistd
+# REQUIRE: netif ipfilter ipfw pf
+
+. /etc/rc.subr
+
+name="blocklistd"
+desc="System blocklist daemon"
+rcvar="blocklistd_enable"
+command="/usr/sbin/${name}"
+required_files="/etc/blocklistd.conf"
+
+# no svcj options needed
+: ${blocklistd_svcj_options:=""}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/bluetooth b/libexec/rc/rc.d/bluetooth
new file mode 100755
index 000000000000..193fd969967f
--- /dev/null
+++ b/libexec/rc/rc.d/bluetooth
@@ -0,0 +1,333 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+# PROVIDE: bluetooth
+# REQUIRE: DAEMON
+# KEYWORD: nojail nostart
+
+. /etc/rc.subr
+
+name="bluetooth"
+desc="Bluetooth setup script"
+rcvar=
+start_cmd="bluetooth_start"
+stop_cmd="bluetooth_stop"
+required_modules="ng_bluetooth ng_hci ng_l2cap ng_btsocket"
+
+##############################################################################
+# Read and parse Bluetooth device configuration file
+##############################################################################
+
+bluetooth_read_conf()
+{
+ local _err _file _line _namespace
+
+ _file=$1
+ _namespace=$2
+ _err=0
+
+ if [ ! -e $_file ]; then
+ return 0
+ fi
+
+ if [ ! -f $_file -o ! -r $_file ]; then
+ err 1 "Bluetooth configuration file $_file is not a file or not readable"
+ fi
+
+ while read _line
+ do
+ case "$_line" in
+ \#*)
+ continue
+ ;;
+
+ *)
+ if [ -z "$_line" ]; then
+ continue;
+ fi
+
+
+ if expr "$_line" : "[a-zA-Z0-9_]*=" > /dev/null 2>&1; then
+ eval "${_namespace}${_line}"
+ else
+ warn "Unable to parse line \"$_line\" in $_file"
+ _err=1
+ fi
+ ;;
+ esac
+ done < $_file
+
+ return $_err
+}
+
+##############################################################################
+# Setup Bluetooth stack. Create and connect nodes
+##############################################################################
+
+bluetooth_setup_stack()
+{
+ dev=$1
+ shift
+ hook=$1
+ shift
+
+ # Setup HCI
+ ngctl mkpeer ${dev}: hci ${hook} drv \
+ > /dev/null 2>&1 || return 1
+
+ ngctl name ${dev}:${hook} ${dev}hci \
+ > /dev/null 2>&1 || return 1
+
+ ngctl msg ${dev}hci: set_debug ${bluetooth_device_hci_debug_level} \
+ > /dev/null 2>&1 || return 1
+
+ # Setup L2CAP
+ ngctl mkpeer ${dev}hci: l2cap acl hci \
+ > /dev/null 2>&1 || return 1
+
+ ngctl name ${dev}hci:acl ${dev}l2cap \
+ > /dev/null 2>&1 || return 1
+
+ ngctl msg ${dev}l2cap: set_debug ${bluetooth_device_l2cap_debug_level} \
+ > /dev/null 2>&1 || return 1
+
+ # Connect HCI node to the Bluetooth sockets layer
+ ngctl connect ${dev}hci: btsock_hci_raw: raw ${dev}raw \
+ > /dev/null 2>&1 || return 1
+
+ # Connect L2CAP node to Bluetooth sockets layer
+ ngctl connect ${dev}l2cap: btsock_l2c_raw: ctl ${dev}ctl \
+ > /dev/null 2>&1 || return 1
+
+ ngctl connect ${dev}l2cap: btsock_l2c: l2c ${dev}l2c \
+ > /dev/null 2>&1 || return 1
+
+ # Initilalize HCI node
+ for loop in 1 2 3
+ do
+ ${hccontrol} -n ${dev}hci reset \
+ > /dev/null 2>&1 && break
+ if [ ${loop} -eq 3 ]
+ then
+ warn Reset failed three times, giving up.
+ return 1
+ fi
+ warn Reset failed, retrying.
+ done
+
+ ${hccontrol} -n ${dev}hci read_bd_addr \
+ > /dev/null 2>&1 || return 1
+
+ ${hccontrol} -n ${dev}hci read_local_supported_features \
+ > /dev/null 2>&1 || return 1
+
+ ${hccontrol} -n ${dev}hci read_buffer_size \
+ > /dev/null 2>&1 || return 1
+
+ if checkyesno bluetooth_device_discoverable; then
+ if checkyesno bluetooth_device_connectable; then
+ ${hccontrol} -n ${dev}hci write_scan_enable 3 \
+ > /dev/null 2>&1 || return 1
+ else
+ ${hccontrol} -n ${dev}hci write_scan_enable 1 \
+ > /dev/null 2>&1 || return 1
+ fi
+ else
+ if checkyesno bluetooth_device_connectable; then
+ ${hccontrol} -n ${dev}hci write_scan_enable 2 \
+ > /dev/null 2>&1 || return 1
+ else
+ ${hccontrol} -n ${dev}hci write_scan_enable 0 \
+ > /dev/null 2>&1 || return 1
+ fi
+ fi
+
+
+ ${hccontrol} -n ${dev}hci write_class_of_device ${bluetooth_device_class} \
+ > /dev/null 2>&1 || return 1
+
+ if checkyesno bluetooth_device_authentication_enable; then
+ ${hccontrol} -n ${dev}hci write_authentication_enable 1 \
+ > /dev/null 2>&1 || return 1
+ else
+ ${hccontrol} -n ${dev}hci write_authentication_enable 0 \
+ > /dev/null 2>&1 || return 1
+ fi
+
+ case "${bluetooth_device_encryption_mode}" in
+ [Nn][Oo][Nn][Ee]|0)
+ ${hccontrol} -n ${dev}hci write_encryption_mode 0 \
+ > /dev/null 2>&1 || return 1
+ ;;
+
+ [Pp][2][Pp]|1)
+ ${hccontrol} -n ${dev}hci write_encryption_mode 1 \
+ > /dev/null 2>&1 || return 1
+ ;;
+
+ [Al][Ll][Ll]|2)
+ ${hccontrol} -n ${dev}hci write_encryption_mode 2 \
+ > /dev/null 2>&1 || return 1
+ ;;
+
+ *)
+ warn "Unsupported encryption mode ${bluetooth_device_encryption_mode} for device ${dev}"
+ return 1
+ ;;
+ esac
+
+ if checkyesno bluetooth_device_role_switch; then
+ ${hccontrol} -n ${dev}hci write_node_role_switch 1 \
+ > /dev/null 2>&1 || return 1
+ else
+ ${hccontrol} -n ${dev}hci write_node_role_switch 0 \
+ > /dev/null 2>&1 || return 1
+ fi
+
+ ${hccontrol} -n ${dev}hci change_local_name "${bluetooth_device_local_name}" \
+ > /dev/null 2>&1 || return 1
+
+ ${hccontrol} -n ${dev}hci initialize \
+ > /dev/null 2>&1 || return 1
+
+ return 0
+}
+
+##############################################################################
+# Shutdown Bluetooth stack. Destroy all nodes
+##############################################################################
+
+bluetooth_shutdown_stack()
+{
+ dev=$1
+
+ ngctl shutdown ${dev}hci: > /dev/null 2>&1
+ ngctl shutdown ${dev}l2cap: > /dev/null 2>&1
+
+ return 0
+}
+
+##############################################################################
+# bluetooth_start()
+##############################################################################
+
+bluetooth_start()
+{
+ local _file
+
+ dev=$1
+
+ # Try to figure out device type by looking at device name
+ case "${dev}" in
+ # USB Bluetooth adapters
+ ubt*)
+ hook="hook"
+
+ # Obtain unit number from device.
+ unit=`expr ${dev} : 'ubt\([0-9]\{1,\}\)'`
+ if [ -z "${unit}" ]; then
+ err 1 "Unable to get ubt unit number: ${dev}"
+ fi
+ ;;
+
+ # Unknown
+ *)
+ err 1 "Unsupported device: ${dev}"
+ ;;
+ esac
+
+ # Be backward compatible and setup reasonable defaults
+ bluetooth_device_authentication_enable="0"
+ bluetooth_device_class="ff:01:0c"
+ bluetooth_device_connectable="1"
+ bluetooth_device_discoverable="0"
+ bluetooth_device_encryption_mode="0"
+ bluetooth_device_hci_debug_level="3"
+ bluetooth_device_l2cap_debug_level="3"
+ bluetooth_device_local_name="`/usr/bin/uname -n` (${dev})"
+ bluetooth_device_role_switch="1"
+
+ # Load default device configuration parameters
+ _file="/etc/defaults/bluetooth.device.conf"
+
+ if ! bluetooth_read_conf $_file bluetooth_device_ ; then
+ err 1 "Unable to read default Bluetooth configuration from $_file"
+ fi
+
+ # Load device specific overrides
+ _file="/etc/bluetooth/$dev.conf"
+
+ if ! bluetooth_read_conf $_file bluetooth_device_ ; then
+ err 1 "Unable to read Bluetooth device configuration from $_file"
+ fi
+
+ # Setup stack
+ if ! bluetooth_setup_stack ${dev} ${hook} ; then
+ bluetooth_shutdown_stack $dev
+ err 1 "Unable to setup Bluetooth stack for device ${dev}"
+ fi
+
+ return 0
+}
+
+##############################################################################
+# bluetooth_stop()
+##############################################################################
+
+bluetooth_stop()
+{
+ dev=$1
+
+ # Try to figure out device type by looking at device name
+ case "${dev}" in
+ # USB Bluetooth adapters
+ ubt*)
+ ;;
+
+ # Unknown
+ *)
+ err 1 "Unsupported device: ${dev}"
+ ;;
+ esac
+
+ bluetooth_shutdown_stack ${dev}
+
+ return 0
+}
+
+##############################################################################
+# Start here
+##############################################################################
+
+load_rc_config $name
+hccontrol="${bluetooth_hccontrol:-/usr/sbin/hccontrol}"
+
+# doesn't make sense to run in a svcj: nojail keyword
+bluetooth_svcj="NO"
+
+run_rc_command $*
+
diff --git a/libexec/rc/rc.d/bootparams b/libexec/rc/rc.d/bootparams
new file mode 100755
index 000000000000..1d435d4ee480
--- /dev/null
+++ b/libexec/rc/rc.d/bootparams
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: bootparams
+# REQUIRE: rpcbind DAEMON
+# BEFORE: LOGIN
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="bootparamd"
+desc="Boot parameter daemon"
+rcvar="bootparamd_enable"
+required_files="/etc/bootparams"
+command="/usr/sbin/${name}"
+
+: ${bootparamd_svcj_options:="net_basic"}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/bridge b/libexec/rc/rc.d/bridge
new file mode 100755
index 000000000000..98d9212593e5
--- /dev/null
+++ b/libexec/rc/rc.d/bridge
@@ -0,0 +1,97 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 The FreeBSD Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE PROJECT BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#
+
+# PROVIDE: bridge
+# REQUIRE: netif ppp stf
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="bridge"
+desc="Network bridge setup"
+start_cmd="bridge_start"
+stop_cmd="bridge_stop"
+cmd=""
+
+glob_int() {
+ case "$1" in
+ $2 ) true ;;
+ * ) false ;;
+ esac
+}
+
+bridge_test() {
+ bridge=$1
+ iface=$2
+
+ eval interfaces=\$autobridge_${bridge}
+ if [ -n "${interfaces}" ]; then
+ for i in ${interfaces}; do
+ if glob_int $iface $i ; then
+ ifconfig $bridge $cmd $iface > /dev/null 2>&1
+ return
+ fi
+ done
+ fi
+}
+
+autobridge()
+{
+ if [ -n "${autobridge_interfaces}" ]; then
+ if [ -z "$iflist" ]; then
+ # We're operating as a general network start routine.
+ iflist="`list_net_interfaces`"
+ fi
+
+ for br in ${autobridge_interfaces}; do
+ for i in $iflist; do
+ bridge_test $br $i
+ done
+ done
+ fi
+}
+
+bridge_start()
+{
+ cmd="addm"
+ autobridge
+}
+
+bridge_stop()
+{
+ cmd="deletem"
+ autobridge
+}
+
+iflist=$2
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+bridge_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/bsnmpd b/libexec/rc/rc.d/bsnmpd
new file mode 100755
index 000000000000..60f4f5e86617
--- /dev/null
+++ b/libexec/rc/rc.d/bsnmpd
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: bsnmpd
+# REQUIRE: NETWORKING syslogd
+# KEYWORD: nojailvnet shutdown
+
+. /etc/rc.subr
+
+name="bsnmpd"
+desc="Simple and extensible SNMP daemon"
+rcvar="bsnmpd_enable"
+command="/usr/sbin/${name}"
+
+: ${bsnmpd_svcj_options:="net_basic"}
+
+load_rc_config $name
+pidfile="${bsnmpd_pidfile:-/var/run/snmpd.pid}"
+command_args="-p ${pidfile}"
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/bthidd b/libexec/rc/rc.d/bthidd
new file mode 100755
index 000000000000..4b230406c4d5
--- /dev/null
+++ b/libexec/rc/rc.d/bthidd
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: bthidd
+# REQUIRE: DAEMON hcsecd
+# BEFORE: LOGIN
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="bthidd"
+desc="Bluetooth HID daemon"
+rcvar="bthidd_enable"
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+start_precmd="bthidd_prestart"
+
+evdev_enabled()
+{
+ case ${bthidd_evdev_support} in
+ [Aa][Uu][Tt][Oo])
+ check_kern_features evdev_support
+ return $?
+ ;;
+ *)
+ checkyesno bthidd_evdev_support
+ return $?
+ ;;
+ esac
+}
+
+bthidd_prestart()
+{
+ if evdev_enabled; then
+ load_kld -m uinput uinput
+ fi
+ load_kld -m kbdmux kbdmux
+ load_kld -m vkbd vkbd
+ load_kld -m ng_btsocket ng_btsocket
+ return 0
+}
+
+load_rc_config $name
+config="${bthidd_config:-/etc/bluetooth/${name}.conf}"
+hids="${bthidd_hids:-/var/db/${name}.hids}"
+command_args="-c ${config} -H ${hids} -p ${pidfile}"
+if evdev_enabled; then
+ command_args="$command_args -u"
+fi
+required_files="${config}"
+
+# doesn't make sense to run in a svcj: nojail keyword
+bthidd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ccd b/libexec/rc/rc.d/ccd
new file mode 100755
index 000000000000..5f2427e4beb0
--- /dev/null
+++ b/libexec/rc/rc.d/ccd
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: disks
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="ccd"
+desc="Concatenated disks setup"
+start_cmd="ccd_start"
+stop_cmd=":"
+
+ccd_start()
+{
+ if [ -f /etc/ccd.conf ]; then
+ echo "Configuring CCD devices."
+ ccdconfig -C
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+ccd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/cfumass b/libexec/rc/rc.d/cfumass
new file mode 100755
index 000000000000..7d1117d7c388
--- /dev/null
+++ b/libexec/rc/rc.d/cfumass
@@ -0,0 +1,152 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: cfumass
+# REQUIRE: var
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="cfumass"
+desc="Configure the LUN for device mode USB mass storage"
+rcvar="cfumass_enable"
+
+start_cmd="${name}_start"
+stop_cmd="${name}_stop"
+
+extra_commands="reload"
+reload_cmd="${name}_start"
+
+: ${cfumass_dir:=/var/cfumass}
+: ${cfumass_image:=/var/tmp/cfumass.img}
+: ${cfumass_vendor:="FreeBSD"}
+: ${cfumass_product:="cfumass(4)"}
+
+remove_luns()
+{
+ local _lun _luns
+
+ _luns=`ctladm devlist -b block -v | awk '
+
+ $1 ~ /^[0-9]+$/ {
+ lun = $1
+ }
+
+ $1 == "file='"${cfumass_image}"'" {
+ print lun
+ }'`
+
+ for _lun in ${_luns}; do
+ ctladm remove -b block -l "${_lun}" > /dev/null
+ done
+}
+
+cfumass_start()
+{
+ local err _files _template _new_template
+
+ if [ ! -d "${cfumass_dir}" ]; then
+ warn "${cfumass_dir} does not exist"
+ return 1
+ fi
+
+ _files=`find "${cfumass_dir}" -newer "${cfumass_image}" -print 2> /dev/null`
+ if [ ! -e "${cfumass_image}" -o -n "${_files}" ]; then
+ # The image doesn't exist or is out of date.
+ makefs -t cd9660 -o label="${cfumass_vendor}" \
+ -o rockridge "${cfumass_image}" "${cfumass_dir}"
+ err=$?
+ if [ "${err}" -ne 0 ]; then
+ warn "unable to create ${cfumass_image}"
+ return "${err}"
+ fi
+ fi
+
+ remove_luns
+
+ ctladm create -b block -o file="${cfumass_image}" -o readonly=on \
+ -o vendor="${cfumass_vendor}" -o product="${cfumass_product}" \
+ -S 0 > /dev/null
+ err=$?
+ if [ "${err}" -ne 0 ]; then
+ warn "unable to create CTL LUN"
+ return "${err}"
+ fi
+
+ load_kld -e cfumass cfumass
+
+ # If the template is already switched to Mass Storage, then reset
+ # it to -1 to force the host to reenumerate it; otherwise it might
+ # not notice the new LUN.
+ _template=`sysctl -n hw.usb.template`
+ if [ "${_template}" -eq 0 ]; then
+ sysctl hw.usb.template=-1 > /dev/null
+ err=$?
+ if [ "${err}" -ne 0 ]; then
+ warn "unable to set hw.usb.template sysctl"
+ return "${err}"
+ fi
+ fi
+
+ # Set the template number based on the current one.
+ _template=`sysctl -n hw.usb.template`
+ case "${_template}" in
+ -1)
+ _new_template="0"
+ ;;
+ 8)
+ _new_template="10"
+ ;;
+ *)
+ warn "hw.usb.template sysctl set to neither -1 nor 8; not changing"
+ _new_template=""
+ ;;
+ esac
+
+ if [ -n "${_new_template}" ]; then
+ sysctl hw.usb.template="${_new_template}" > /dev/null
+ err=$?
+ if [ "${err}" -ne 0 ]; then
+ warn "unable to set hw.usb.template sysctl to ${_new_template}"
+ return "${err}"
+ fi
+ fi
+}
+
+cfumass_stop()
+{
+ local err _template _new_template
+
+ remove_luns
+
+ _template=`sysctl -n hw.usb.template`
+ case "${_template}" in
+ 0)
+ _new_template="-1"
+ ;;
+ 10)
+ _new_template="8"
+ ;;
+ *)
+ warn "hw.usb.template sysctl set to neither 0 nor 10; not changing"
+ _new_template=""
+ ;;
+ esac
+
+ if [ -n "${_new_template}" ]; then
+ sysctl hw.usb.template="${_new_template}" > /dev/null
+ err=$?
+ if [ "${err}" -ne 0 ]; then
+ warn "unable to set hw.usb.template sysctl to ${_new_template}"
+ return "${err}"
+ fi
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+cfumass_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/cleanvar b/libexec/rc/rc.d/cleanvar
new file mode 100755
index 000000000000..dce5baa6875b
--- /dev/null
+++ b/libexec/rc/rc.d/cleanvar
@@ -0,0 +1,50 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: cleanvar
+# REQUIRE: var
+
+. /etc/rc.subr
+
+name="cleanvar"
+desc="Purge /var directory"
+rcvar="cleanvar_enable"
+
+start_precmd="${name}_prestart"
+start_cmd="${name}_start"
+stop_cmd=":"
+
+extra_commands="reload"
+reload_cmd="${name}_start"
+
+cleanvar_prestart()
+{
+ # These files must be removed only the first time this script is run
+ # on boot.
+ #
+ rm -f /var/run/clean_var /var/spool/lock/clean_var
+}
+
+cleanvar_start()
+{
+ if [ -d /var/run -a ! -f /var/run/clean_var ]; then
+ # Skip over logging sockets
+ find -x /var/run \( -type f -or -type s ! -name log -and ! -name logpriv \) -delete
+ >/var/run/clean_var
+ fi
+ if [ -d /var/spool/lock -a ! -f /var/spool/lock/clean_var ]; then
+ find -x /var/spool/lock -type f -delete
+ >/var/spool/lock/clean_var
+ fi
+ if [ -d /var/spool/uucp/.Temp ]; then
+ find -x /var/spool/uucp/.Temp -delete
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+cleanvar_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/cleartmp b/libexec/rc/rc.d/cleartmp
new file mode 100755
index 000000000000..c4dfb5367dcb
--- /dev/null
+++ b/libexec/rc/rc.d/cleartmp
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: cleartmp
+# REQUIRE: mountcritremote tmp
+# BEFORE: DAEMON
+
+. /etc/rc.subr
+
+name="cleartmp"
+desc="Purge /tmp directory"
+# Disguise rcvar for the start method to run irrespective of its setting.
+rcvar1="clear_tmp_enable"
+start_cmd="${name}_start"
+stop_cmd=":"
+
+cleartmp_start()
+{
+ # Make /tmp location variable for easier debugging.
+ local tmp="/tmp"
+
+ # X related directories to create in /tmp.
+ local x11_socket_dirs="${tmp}/.X11-unix ${tmp}/.XIM-unix \
+ ${tmp}/.ICE-unix ${tmp}/.font-unix"
+
+ if checkyesno ${rcvar1}; then
+ startmsg "Clearing ${tmp}."
+
+ # This is not needed for mfs, but doesn't hurt anything.
+ # Things to note:
+ # + The dot in ${tmp}/. is important.
+ # + Put -prune before -exec so find never descends
+ # into a directory that was already passed to rm -rf.
+ # + "--" in rm arguments isn't strictly necessary, but
+ # it can prevent foot-shooting in future.
+ # + /tmp/lost+found is preserved, but its contents are removed.
+ # + lost+found and quota.* in subdirectories are removed.
+ # + .sujournal and .snap are preserved.
+ find -x ${tmp}/. ! -name . \
+ ! \( -name .sujournal -type f -user root \) \
+ ! \( -name .snap -type d -user root \) \
+ ! \( -name lost+found -type d -user root \) \
+ ! \( \( -name quota.user -or -name quota.group \) \
+ -type f -user root \) \
+ -prune -exec rm -rf -- {} +
+ elif checkyesno clear_tmp_X; then
+ # Remove X lock files, since they will prevent you from
+ # restarting X. Remove other X related directories.
+ startmsg "Clearing ${tmp} (X related)."
+ rm -rf ${tmp}/.X[0-9]-lock ${x11_socket_dirs}
+ fi
+ if checkyesno clear_tmp_X; then
+ # Create X related directories with proper permissions.
+ mkdir -m 1777 ${x11_socket_dirs}
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+cleartmp_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/cron b/libexec/rc/rc.d/cron
new file mode 100755
index 000000000000..584db590d835
--- /dev/null
+++ b/libexec/rc/rc.d/cron
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: cron
+# REQUIRE: LOGIN FILESYSTEMS
+# BEFORE: securelevel
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="cron"
+desc="Daemon to execute scheduled commands"
+rcvar="cron_enable"
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: in the generic case it may need
+# access to more than a jails allows
+cron_svcj="NO"
+
+if checkyesno cron_dst
+then
+ cron_flags="$cron_flags -s"
+fi
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ctld b/libexec/rc/rc.d/ctld
new file mode 100755
index 000000000000..c91d7a9be921
--- /dev/null
+++ b/libexec/rc/rc.d/ctld
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ctld
+# REQUIRE: FILESYSTEMS NETWORKING
+# BEFORE: DAEMON
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="ctld"
+desc="CAM Target Layer / iSCSI target daemon"
+rcvar="ctld_enable"
+pidfile="/var/run/${name}.pid"
+command="/usr/sbin/${name}"
+required_files="/etc/ctl.conf"
+required_modules="ctl"
+extra_commands="reload"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+ctld_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ddb b/libexec/rc/rc.d/ddb
new file mode 100755
index 000000000000..08a7d345c326
--- /dev/null
+++ b/libexec/rc/rc.d/ddb
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ddb
+# REQUIRE: dumpon
+# BEFORE: disks
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="ddb"
+desc="DDB kernel debugger"
+rcvar="ddb_enable"
+command="/sbin/${name}"
+start_precmd="ddb_prestart"
+start_cmd="ddb_start"
+stop_cmd=":"
+
+ddb_prestart()
+{
+ # Silently exit if ddb is not enabled
+ if [ -z "`sysctl -Nq debug.ddb.scripting.scripts`" ]; then
+ return 1
+ fi
+}
+
+ddb_start()
+{
+ ${command} ${command_args}
+}
+
+load_rc_config $name
+
+required_files="${ddb_config}"
+command_args="${ddb_config}"
+
+# doesn't make sense to run in a svcj: privileged operation
+ddb_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/defaultroute b/libexec/rc/rc.d/defaultroute
new file mode 100755
index 000000000000..b96f91d36118
--- /dev/null
+++ b/libexec/rc/rc.d/defaultroute
@@ -0,0 +1,77 @@
+#!/bin/sh
+#
+# Wait for the default route to be up if DHCP is in use
+#
+#
+
+# PROVIDE: defaultroute
+# REQUIRE: devd netif stf
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="defaultroute"
+desc="Setup default router"
+start_cmd="defaultroute_start"
+stop_cmd=":"
+
+# Does any interface have a carrier?
+defaultroute_carrier()
+{
+ local carrier nocarrier
+
+ carrier=1
+ for _if in ${dhcp_interfaces}; do
+ output=`/sbin/ifconfig ${_if}`
+ nocarrier=`expr "${output}" : '.*[[:blank:]]status: \(no carrier\)'`
+ [ -z "${nocarrier}" ] && carrier=0
+ done
+ return ${carrier}
+}
+
+defaultroute_start()
+{
+ local nl waited
+
+ afexists inet || return 0
+
+ # Return without waiting if we don't have dhcp interfaces or
+ # if none of the dhcp interfaces is plugged in.
+ dhcp_interfaces=`list_net_interfaces dhcp`
+ [ -z "${dhcp_interfaces}" ] && return
+
+ # Wait for a default route
+ waited=0
+ while [ ${waited} -lt ${defaultroute_delay} ]; do
+ defif=`get_default_if -inet`
+ if [ -n "${defif}" ]; then
+ if [ ${waited} -ne 0 ]; then
+ echo -n "($defif)"
+ nl=1
+ fi
+ break
+ fi
+ if [ ${waited} -eq 0 ]; then
+ echo -n "Waiting ${defaultroute_delay}s for the default route interface: "
+ else
+ echo -n .
+ fi
+ if [ ${waited} -eq ${defaultroute_carrier_delay} ] && ! defaultroute_carrier; then
+ echo -n "(no carrier)"
+ break
+ fi
+ nl=1
+ sleep 1
+ waited=$(($waited + 1))
+ done
+
+ [ -n "$nl" ] && echo
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+defaultroute_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/devd b/libexec/rc/rc.d/devd
new file mode 100755
index 000000000000..98f2068c2075
--- /dev/null
+++ b/libexec/rc/rc.d/devd
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: devd
+# REQUIRE: netif ldconfig
+# BEFORE: NETWORKING mountcritremote
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="devd"
+desc="Device state change daemon"
+rcvar="devd_enable"
+command="/sbin/${name}"
+
+devd_offcmd=devd_off
+start_precmd=find_pidfile
+stop_precmd=find_pidfile
+
+find_pidfile()
+{
+ if get_pidfile_from_conf pid-file /etc/devd.conf; then
+ pidfile="$_pidfile_from_conf"
+ else
+ pidfile="/var/run/${name}.pid"
+ fi
+}
+
+devd_off()
+{
+ # If devd is disabled, turn it off in the kernel to avoid unnecessary
+ # memory usage.
+ if ! checkyesno ${rcvar}; then
+ $SYSCTL hw.bus.devctl_queue=0
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: executing potential privileged operations
+devd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/devfs b/libexec/rc/rc.d/devfs
new file mode 100755
index 000000000000..9987d35f6ad3
--- /dev/null
+++ b/libexec/rc/rc.d/devfs
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: devfs
+# REQUIRE: mountcritremote
+# BEFORE: SERVERS securelevel
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="devfs"
+desc="Device filesystem"
+start_cmd='devfs_start'
+stop_cmd=':'
+
+devfs_start()
+{
+ if [ -n "$devfs_system_ruleset" -o -n "$devfs_set_rulesets" ] ||
+ checkyesno devfs_load_rulesets; then
+ devfs_init_rulesets
+ if [ -n "$devfs_system_ruleset" ]; then
+ devfs_set_ruleset $devfs_system_ruleset /dev
+ devfs_apply_ruleset $devfs_system_ruleset /dev
+ fi
+ if [ -n "$devfs_set_rulesets" ]; then
+ local _dir_set
+ local _dir
+ local _set
+ for _dir_set in $devfs_set_rulesets; do
+ _dir=${_dir_set%=*}
+ _set=${_dir_set#*=}
+ devfs_set_ruleset $_set $_dir
+ devfs_apply_ruleset $_set $_dir
+ done
+ fi
+ fi
+ read_devfs_conf
+}
+
+read_devfs_conf()
+{
+ if [ -r /etc/devfs.conf ]; then
+ cd /dev
+ while read action devicelist parameter; do
+ case "${action}" in
+ l*) for device in ${devicelist}; do
+ if [ ! -e ${parameter} ]; then
+ ln -fs ${device} ${parameter}
+ fi
+ done
+ ;;
+ o*) for device in ${devicelist}; do
+ if [ -c ${device} ]; then
+ chown ${parameter} ${device}
+ fi
+ done
+ ;;
+ p*) for device in ${devicelist}; do
+ if [ -c ${device} ]; then
+ chmod ${parameter} ${device}
+ fi
+ done
+ ;;
+ esac
+ done < /etc/devfs.conf
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: may need more permissions
+devfs_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/devmatch b/libexec/rc/rc.d/devmatch
new file mode 100755
index 000000000000..7a8726de5677
--- /dev/null
+++ b/libexec/rc/rc.d/devmatch
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+# Copyright (c) 2018 M. Warner Losh <imp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+# PROVIDE: devmatch
+# REQUIRE: kld
+# BEFORE: netif
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="devmatch"
+desc="Use devmatch(8) to load kernel modules"
+rcvar="${name}_enable"
+
+start_cmd="${name}_start"
+stop_cmd=':'
+one_nomatch="$2"
+
+devmatch_start()
+{
+ local x m list boot_safe
+
+ boot_safe=$(kenv -q boot_safe || echo "NO")
+ checkyesno boot_safe && return
+
+ if [ -n "$one_nomatch" ]; then
+ list=$(devmatch -p "${one_nomatch}" | sort -u)
+ else
+ sysctl hw.bus.devctl_nomatch_enabled=1 > /dev/null
+ list=$(devmatch | sort -u)
+ fi
+
+ [ -n "$list" ] || return
+
+ # While kldload can accept multiple modules on the line at once, we loop
+ # here in case there's some weird error with one of them. We also
+ # optimize against the false positives or drivers that have symbolic
+ # links that confuse devmatch by running it -n. Finally, we filter out
+ # all items in the devmatch_blocklist.
+ #
+ # We strip all the .ko suffixes off so that one may specify modules
+ # with or without .ko. Prior version documented it was without, while
+ # the code required it, so accept both now. devmatch produces module
+ # names with .ko
+
+ devctl freeze
+ x=$(echo "#${devmatch_blocklist:-${devmatch_blacklist}}#$(kenv -q devmatch_blocklist)#" | \
+ sed -e "s/ /#/g;s/\.ko#/#/g")
+ for m in ${list}; do
+ m="${m%.ko}"
+ case "${x}" in
+ *"#${m}#"*) continue ;;
+ esac
+ kldstat -q -n ${m} || \
+ (echo "Autoloading module: ${m}"; kldload -n ${m})
+ done
+ devctl thaw
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: privileged operations
+devmatch_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/dhclient b/libexec/rc/rc.d/dhclient
new file mode 100755
index 000000000000..1cd770031d71
--- /dev/null
+++ b/libexec/rc/rc.d/dhclient
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: dhclient
+# KEYWORD: nojailvnet nostart
+
+. /etc/rc.subr
+. /etc/network.subr
+
+ifn="$2"
+
+name="dhclient"
+desc="Dynamic Host Configuration Protocol (DHCP) client"
+rcvar=
+pidfile="/var/run/dhclient/${name}.${ifn}.pid"
+start_precmd="dhclient_prestart"
+stop_precmd="dhclient_pre_check"
+
+# rc_force check can only be done at the run_rc_command
+# time, so we're testing it in the pre* hooks.
+dhclient_pre_check()
+{
+ if [ -z "${rc_force}" ] && ! dhcpif $ifn; then
+ local msg
+ msg="'$ifn' is not a DHCP-enabled interface"
+ if [ -z "${rc_quiet}" ]; then
+ echo "$msg"
+ else
+ debug "$msg"
+ fi
+ exit 1
+ fi
+}
+
+dhclient_prestart()
+{
+ dhclient_pre_check
+
+ # Interface-specific flags (see rc.subr for $flags setting)
+ specific=$(get_if_var $ifn dhclient_flags_IF)
+ if [ -z "$flags" -a -n "$specific" ]; then
+ rc_flags=$specific
+ fi
+
+ background_dhclient=$(get_if_var $ifn background_dhclient_IF $background_dhclient)
+ if checkyesno background_dhclient; then
+ rc_flags="${rc_flags} -b"
+ fi
+
+ dhclient_arpwait=$(get_if_var $ifn dhclient_arpwait_IF $dhclient_arpwait)
+ if ! checkyesno dhclient_arpwait; then
+ rc_flags="${rc_flags} -n"
+ fi
+
+ # /var/run/dhclient is not guaranteed to exist,
+ # e.g. if /var/run is a tmpfs
+ install -d -o root -g wheel -m 755 ${pidfile%/*}
+
+ rc_flags="${rc_flags} ${ifn}"
+}
+
+load_rc_config $name
+load_rc_config network
+
+# dhclient_prestart is not compatible with svcj
+dhclient_svcj="NO"
+
+if [ -z $ifn ] ; then
+ # only complain if a command was specified but no interface
+ if [ -n "$1" ] ; then
+ err 1 "$0: no interface specified"
+ fi
+fi
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/dmesg b/libexec/rc/rc.d/dmesg
new file mode 100755
index 000000000000..736449f3b159
--- /dev/null
+++ b/libexec/rc/rc.d/dmesg
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: dmesg
+# REQUIRE: mountcritremote FILESYSTEMS
+# BEFORE: DAEMON
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="dmesg"
+desc="Save kernel boot messages to disk"
+rcvar="dmesg_enable"
+dmesg_file="/var/run/dmesg.boot"
+start_cmd="do_dmesg"
+stop_cmd=":"
+
+do_dmesg()
+{
+ rm -f ${dmesg_file}
+ ( umask "${dmesg_umask}" ; /sbin/dmesg $rc_flags > ${dmesg_file} )
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+dmesg_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/dnctl b/libexec/rc/rc.d/dnctl
new file mode 100755
index 000000000000..9067d278088e
--- /dev/null
+++ b/libexec/rc/rc.d/dnctl
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: dnctl
+# BEFORE: pf ipfw
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="dnctl"
+desc="Dummynet packet queuing and scheduling"
+rcvar="${name}_enable"
+load_rc_config $name
+start_cmd="${name}_start"
+required_files="$dnctl_rules"
+required_modules="dummynet"
+
+# doesn't make sense to run in a svcj: config setting
+dnctl_svcj="NO"
+
+dnctl_start()
+{
+ startmsg -n "Enabling ${name}"
+ $dnctl_program "$dnctl_rules"
+ startmsg '.'
+}
+
+run_rc_command $*
diff --git a/libexec/rc/rc.d/dumpon b/libexec/rc/rc.d/dumpon
new file mode 100755
index 000000000000..0dfcdb266b20
--- /dev/null
+++ b/libexec/rc/rc.d/dumpon
@@ -0,0 +1,104 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: dumpon
+# BEFORE: disks
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="dumpon"
+desc="Dump kernel corefiles from swap to disk"
+start_cmd="dumpon_start"
+stop_cmd="dumpon_stop"
+
+dumpon_try()
+{
+ local flags
+
+ flags=${dumpon_flags}
+ if [ -n "${dumppubkey}" ]; then
+ warn "The dumppubkey variable is deprecated. Use dumpon_flags."
+ flags="${flags} -k ${dumppubkey}"
+ fi
+ /sbin/dumpon ${flags} "${1}"
+ if [ $? -eq 0 ]; then
+ # Make a symlink in devfs for savecore
+ ln -fs "${1}" /dev/dumpdev
+ return 0
+ fi
+ warn "unable to specify $1 as a dump device"
+ return 1
+}
+
+dumpon_warn_unencrypted()
+{
+ if [ -n "${dumppubkey}" ]; then
+ return
+ fi
+ for flag in ${dumpon_flags}; do
+ if [ $flag = -k ]; then
+ return
+ fi
+ done
+ warn "Kernel dumps will be written to the swap partition without encryption."
+}
+
+dumpon_start()
+{
+ # Enable dumpdev so that savecore can see it. Enable it
+ # early so a crash early in the boot process can be caught.
+ #
+ case ${dumpdev} in
+ [Nn][Oo])
+ ;;
+ [Aa][Uu][Tt][Oo] | '')
+ root_hold_wait
+ dev=$(/bin/kenv -q dumpdev)
+ if [ -n "${dev}" ] ; then
+ dumpon_try "${dev}"
+ return $?
+ fi
+ if [ -z ${dumpdev} ] ; then
+ return
+ fi
+ while read dev mp type more ; do
+ [ "${type}" = "swap" ] || continue
+ case ${dev} in
+ *.bde|*.eli)
+ dumpon_warn_unencrypted
+ dev=${dev%.*}
+ ;;
+ esac
+ [ -c "${dev}" ] || continue
+ dumpon_try "${dev}" 2>/dev/null && return 0
+ done </etc/fstab
+ echo "No suitable dump device was found." 1>&2
+ return 1
+ ;;
+ *)
+ root_hold_wait
+ dumpon_try "${dumpdev}"
+ ;;
+ esac
+}
+
+dumpon_stop()
+{
+ case ${dumpdev} in
+ [Nn][Oo])
+ ;;
+ *)
+ rm -f /dev/dumpdev
+ /sbin/dumpon -v off
+ ;;
+ esac
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+dumpon_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/fsck b/libexec/rc/rc.d/fsck
new file mode 100755
index 000000000000..e755f055dbe6
--- /dev/null
+++ b/libexec/rc/rc.d/fsck
@@ -0,0 +1,98 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: fsck
+# REQUIRE: swap
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="fsck"
+desc="Run file system checks"
+start_cmd="fsck_start"
+stop_cmd=":"
+
+fsck_start()
+{
+ if [ "$autoboot" = no ]; then
+ echo "Fast boot: skipping disk checks."
+ elif [ ! -r /etc/fstab ]; then
+ echo "Warning! No /etc/fstab: skipping disk checks."
+ elif [ "$autoboot" = yes ]; then
+ # During fsck ignore SIGQUIT
+ trap : 3
+
+ startmsg "Starting file system checks:"
+ # Background fsck can only be run with -p
+ if checkyesno background_fsck; then
+ fsck -F -p
+ else
+ fsck ${fsck_flags}
+ fi
+
+ err=$?
+ if [ ${err} -eq 3 ]; then
+ echo "Warning! Some of the devices might not be" \
+ "available; retrying"
+ root_hold_wait
+ startmsg "Restarting file system checks:"
+ # Background fsck can only be run with -p
+ if checkyesno background_fsck; then
+ fsck -F -p
+ else
+ fsck ${fsck_flags}
+ fi
+ err=$?
+ fi
+
+ case ${err} in
+ 0)
+ ;;
+ 2)
+ stop_boot
+ ;;
+ 4)
+ echo "Rebooting..."
+ reboot
+ echo "Reboot failed; help!"
+ stop_boot
+ ;;
+ 8|16)
+ if checkyesno fsck_y_enable; then
+ echo "File system preen failed, trying fsck -y ${fsck_y_flags}"
+ fsck -y ${fsck_y_flags}
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Automatic file system check failed; help!"
+ stop_boot
+ ;;
+ esac
+ else
+ echo "Automatic file system check failed; help!"
+ stop_boot
+ fi
+ ;;
+ 12)
+ echo "Boot interrupted."
+ stop_boot
+ ;;
+ 130)
+ stop_boot
+ ;;
+ *)
+ echo "Unknown error ${err}; help!"
+ stop_boot
+ ;;
+ esac
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+fsck_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ftp-proxy b/libexec/rc/rc.d/ftp-proxy
new file mode 100755
index 000000000000..c77dd36cd60b
--- /dev/null
+++ b/libexec/rc/rc.d/ftp-proxy
@@ -0,0 +1,77 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ftp-proxy
+# REQUIRE: DAEMON pf
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="ftpproxy"
+desc="Internet File Transfer Protocol proxy daemon"
+rcvar="ftpproxy_enable"
+command="/usr/sbin/ftp-proxy"
+
+: ${ftpproxy_svcj_options:="net_basic"}
+
+load_rc_config $name
+
+#
+# manage_pid argument
+# Create or remove a pidfile manually, for daemons that can't be bothered
+# to do it themselves. Takes one argument, which is the argument provided
+# to the rc script. The pidfile will be named /var/run/<$name>.pid,
+# unless $pidfile is defined.
+#
+# The method used to determine the pid is rather hacky; grep ps output to
+# find '$procname|$command', then grep for ${name}_flags. If at all
+# possible, use another method if at all possible, to avoid that dirty-
+# code feeling.
+#
+manage_pid() {
+ local search_string ps_pid
+ case $1 in
+ *start)
+ cmd_string=`basename ${procname:-${command}}`
+ eval flag_string=\"\$${name}_flags\"
+ # Determine the pid.
+ ps_pid=`ps ax -o pid= -o command= | grep $cmd_string | grep -e "$flag_string" | grep -v grep | awk '{ print $1 }'`
+ # Write the pidfile depending on $pidfile status.
+ echo $ps_pid > ${pidfile:-"/var/run/$name.pid"}
+ ;;
+ stop)
+ rm $pidfile
+ ;;
+ esac
+}
+
+# Allow ftp-proxy to start up in two different ways. The typical behavior
+# is to start up one instance of ftp-proxy by setting ftpproxy_enable and
+# ftpproxy_flags. The alternate behavior allows multiple instances of ftp-
+# proxy to be started, allowing different types of proxy behavior. To use the
+# new behavior, a list of instances must be defined, and a list of flags for
+# each instance. For example, if we want to start two instances of ftp-proxy,
+# foo and bar, we would set the following vars.
+# ftpproxy_enable="YES"
+# ftpproxy_instances="foo bar"
+# ftpproxy_foo="<arguments for foo>"
+# ftpproxy_bar="<arguments for bar>"
+#
+# Starting more than one ftp-proxy?
+if [ "$ftpproxy_instances" ] && [ -n "${ftpproxy_instances}" ]; then
+ # Iterate through instance list.
+ for i in $ftpproxy_instances; do
+ #eval ftpproxy_${i}_flags=\$ftpproxy_${i}
+ #eval name=ftpproxy_${i}
+ # Set flags for this instance.
+ eval ftpproxy_flags=\$ftpproxy_${i}
+ # Define a unique pid file name.
+ pidfile="/var/run/ftp-proxy.$i.pid"
+ run_rc_command "$1"
+ manage_pid $1
+ done
+else
+ # Traditional single-instance behavior
+ run_rc_command "$1"
+fi
diff --git a/libexec/rc/rc.d/geli b/libexec/rc/rc.d/geli
new file mode 100755
index 000000000000..5fc5ded54ec3
--- /dev/null
+++ b/libexec/rc/rc.d/geli
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: disks
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="geli"
+desc="GELI disk encryption"
+start_precmd='[ -n "$(geli_make_list)" -o -n "${geli_groups}" ]'
+start_cmd="geli_start"
+stop_cmd="geli_stop"
+required_modules="geom_eli:g_eli"
+
+geli_start()
+{
+ devices=`geli_make_list`
+
+ if [ -z "${geli_tries}" ]; then
+ if [ -n "${geli_attach_attempts}" ]; then
+ geli_tries=${geli_attach_attempts}
+ else
+ geli_tries=`${SYSCTL_N} kern.geom.eli.tries`
+ fi
+ fi
+
+ for provider in ${devices}; do
+ provider_=`ltr ${provider} '/-' '_'`
+
+ eval "flags=\${geli_${provider_}_flags}"
+ if [ -z "${flags}" ]; then
+ flags=${geli_default_flags}
+ fi
+ if [ -e "/dev/${provider}" -a ! -e "/dev/${provider}.eli" ]; then
+ echo "Configuring Disk Encryption for ${provider}."
+ count=1
+ while [ ${count} -le ${geli_tries} ]; do
+ geli attach ${flags} ${provider}
+ if [ -e "/dev/${provider}.eli" ]; then
+ break
+ fi
+ echo "Attach failed; attempt ${count} of ${geli_tries}."
+ count=$((count+1))
+ done
+ fi
+ done
+
+ for group in ${geli_groups}; do
+ group_=`ltr ${group} '/-' '_'`
+
+ eval "flags=\${geli_${group_}_flags}"
+ if [ -z "${flags}" ]; then
+ flags=${geli_default_flags}
+ fi
+
+ eval "providers=\${geli_${group_}_devices}"
+ if [ -z "${providers}" ]; then
+ echo "No devices listed in geli group ${group}."
+ continue
+ fi
+
+ if [ -e "/dev/${providers%% *}" -a ! -e "/dev/${providers%% *}.eli" ]; then
+ echo "Configuring Disk Encryption for geli group ${group}, containing ${providers}."
+ count=1
+ while [ ${count} -le ${geli_tries} ]; do
+ geli attach ${flags} ${providers}
+ if [ -e "/dev/${providers%% *}.eli" ]; then
+ break
+ fi
+ echo "Attach failed; attempt ${count} of ${geli_tries}."
+ count=$((count+1))
+ done
+ fi
+ done
+}
+
+geli_stop()
+{
+ devices=`geli_make_list`
+
+ for group in ${geli_groups}; do
+ group_=`ltr ${group} '/-' '_'`
+
+ eval "providers=\${geli_${group_}_devices}"
+
+ devices="${devices} ${providers}"
+ done
+
+ for provider in ${devices}; do
+ if [ -e "/dev/${provider}.eli" ]; then
+ umount "/dev/${provider}.eli" 2>/dev/null
+ geli detach "${provider}"
+ fi
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+geli_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/geli2 b/libexec/rc/rc.d/geli2
new file mode 100755
index 000000000000..cedd48a312ee
--- /dev/null
+++ b/libexec/rc/rc.d/geli2
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: geli2
+# REQUIRE: FILESYSTEMS
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="geli2"
+desc="GELI disk encryption"
+start_cmd="geli2_start"
+stop_cmd=":"
+
+geli2_start()
+{
+ devices=`geli_make_list`
+
+ for provider in ${devices}; do
+ provider_=`ltr ${provider} '/-' '_'`
+
+ eval "autodetach=\${geli_${provider_}_autodetach}"
+ if [ -z "${autodetach}" ]; then
+ autodetach=${geli_autodetach}
+ fi
+ if checkyesno autodetach && [ -e "/dev/${provider}.eli" ]; then
+ geli detach -l ${provider}
+ fi
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+geli2_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ggated b/libexec/rc/rc.d/ggated
new file mode 100755
index 000000000000..846019acb055
--- /dev/null
+++ b/libexec/rc/rc.d/ggated
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# PROVIDE: ggated
+# REQUIRE: NETWORKING
+
+. /etc/rc.subr
+
+name="ggated"
+desc="GEOM Gate network daemon"
+rcvar="ggated_enable"
+command="/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+
+load_rc_config $name
+required_files="${ggated_config}"
+
+# XXX?: doesn't make sense to run in a svcj: low-level access
+ggated_svcj="NO"
+
+command_args="${ggated_config}"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/gptboot b/libexec/rc/rc.d/gptboot
new file mode 100755
index 000000000000..188f1bb77557
--- /dev/null
+++ b/libexec/rc/rc.d/gptboot
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: gptboot
+# REQUIRE: mountcritremote
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="gptboot"
+rcvar="gptboot_enable"
+start_cmd="gptboot_report"
+
+gptboot_report()
+{
+ gpart show | \
+ egrep '(^=>| freebsd-ufs .*(\[|,)(bootfailed|bootonce)(,|\]))' | \
+ sed 's/^=>//' | \
+ egrep -v '(\[|,)bootme(,|\])' | \
+ while read start size pos type attrs rest; do
+ case "${pos}" in
+ [0-9]*)
+ if [ -n "${disk}" ]; then
+ part="${disk}p${pos}"
+ echo "${attrs}" | egrep -q '(\[|,)bootfailed(,|\])'
+ bootfailed=$?
+ echo "${attrs}" | egrep -q '(\[|,)bootonce(,|\])'
+ bootonce=$?
+ if [ ${bootfailed} -eq 0 ]; then
+ logger -t gptboot -p local0.notice "Boot from ${part} failed."
+ gpart unset -a bootfailed -i ${pos} ${disk} >/dev/null
+ elif [ ${bootonce} -eq 0 ]; then
+ # We want to log success after all failures.
+ echo -n "Boot from ${part} succeeded."
+ gpart unset -a bootonce -i ${pos} ${disk} >/dev/null
+ fi
+ fi
+ ;;
+ *)
+ if [ "${type}" = "GPT" ]; then
+ disk="${pos}"
+ else
+ disk=""
+ fi
+ ;;
+ esac
+ done | logger -t gptboot -p local0.notice
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+gptboot_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/growfs b/libexec/rc/rc.d/growfs
new file mode 100755
index 000000000000..86bf199a8611
--- /dev/null
+++ b/libexec/rc/rc.d/growfs
@@ -0,0 +1,313 @@
+#!/bin/sh
+#
+# Copyright 2022 Michael J. Karels
+# Copyright 2014 John-Mark Gurney
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: growfs
+# REQUIRE: fsck
+# BEFORE: root
+# KEYWORD: firstboot
+
+# Grow root partition to fill available space, optionally adding a swap
+# partition at the end. This allows us to distribute an image and
+# have it work on essentially any size drive.
+
+# Note that this uses awk(1), and thus will not work if /usr is on a separate
+# filesystem. We need to run early, because there might be not enough free
+# space on rootfs for the boot to succeed, and on images we ship - which are
+# the primary purpose of this script - there is no separate /usr anyway.
+
+. /etc/rc.subr
+
+name="growfs"
+desc="Grow root partition to fill device"
+start_cmd="growfs_start"
+stop_cmd=":"
+rcvar="growfs_enable"
+
+growfs_get_diskdev()
+{
+ local _search=${1}
+ sysctl -b kern.geom.conftxt |
+ while read x1 _type _dev line
+ do
+ if [ "${_type}" = "DISK" -a -n "$(echo ${_search} | grep ${_dev})" ]; then
+ echo -n ${_dev}
+ break
+ fi
+ done
+}
+
+# Compute upper bound on swap partition size (if added), based on physmem
+# and vm.swap_maxpages / 2 (the limit that elicits a warning).
+# Rule for swap size based on memory size:
+# up to 4 GB twice memory size
+# 4 GB - 8 GB 8 GB
+# over 8 GB memory size
+growfs_swap_max()
+{
+ memsize=$(sysctl -n hw.physmem)
+ memsizeMB=$(($memsize / (1024 * 1024)))
+
+ if [ $memsizeMB -lt 4096 ]
+ then
+ swapmax=$(($memsize * 2))
+ elif [ $memsizeMB -lt 8192 ]
+ then
+ swapmax=$((8192 * 1024 * 1024))
+ else
+ swapmax=$memsize
+ fi
+
+ pagesize=$(sysctl -n hw.pagesize)
+ vm_swap_max=$(($(sysctl -n vm.swap_maxpages) / 2 * $pagesize))
+
+ if [ $swapmax -gt $vm_swap_max ]
+ then
+ swapmax=$vm_swap_max
+ fi
+ echo -n "$swapmax"
+}
+
+# Find newly-added swap partition on parent device ($1).
+growfs_last_swap()
+{
+ swapdev=$(gpart list $1 | awk '
+ $2 == "Name:" { dev = $3 }
+ $1 == "type:" && $2 == "freebsd-swap" { swapdev = dev }
+ END { print swapdev }
+ ')
+ echo -n $swapdev
+}
+
+growfs_start()
+{
+ verbose=0
+ echo "Growing root partition to fill device"
+ FSTYPE=$(mount -p | awk '{ if ( $2 == "/") { print $3 }}')
+ FSDEV=$(mount -p | awk '{ if ( $2 == "/") { print $1 }}')
+ case "$FSTYPE" in
+ ufs)
+ rootdev=${FSDEV#/dev/}
+ ;;
+ zfs)
+ pool=${FSDEV%%/*}
+ rootdev=$(zpool list -v $pool | awk 'END { print $1 }')
+ ;;
+ *)
+ echo "Don't know how to grow root filesystem type: $FSTYPE"
+ return
+ esac
+ if [ x"$rootdev" = x"${rootdev%/*}" ]; then
+ # raw device
+ rawdev="$rootdev"
+ else
+ rawdev=$(glabel status | awk -v rootdev=$rootdev 'index(rootdev, $1) { print $3; }')
+ if [ x"$rawdev" = x"" ]; then
+ echo "Can't figure out device for: $rootdev"
+ return
+ fi
+ fi
+
+ if [ x"diskid" = x"${rootdev%/*}" ]; then
+ search=$rootdev
+ else
+ search=$rawdev
+ fi
+
+ diskdev=$(growfs_get_diskdev ${search})
+ if [ -z "${diskdev}" ]; then
+ diskdev=${rootdev}
+ fi
+
+ # Check kenv for growfs_swap_size; if not present,
+ # check $growfs_swap_size from /etc/rc.conf.
+ # A value of 0 suppresses swap addition,
+ # "" (or unset) specifies the default;
+ # other values indicate the size in bytes.
+ # If default, check whether swap is already in fstab;
+ # if so, don't add another.
+ addswap=1
+ swapsize="$(kenv -q growfs_swap_size 2>/dev/null)"
+ case "$swapsize" in
+ "0") addswap=0
+ ;;
+ "") case "$growfs_swap_size" in
+ "0") addswap=0
+ ;;
+ "")
+ if ! awk '
+ /^#/ { next }
+ $3 == "swap" { exit 1 }
+ ' < /etc/fstab
+ then
+ addswap=0
+ fi
+ ;;
+ *) swapsize="$growfs_swap_size"
+ ;;
+ esac
+ ;;
+ *) ;;
+ esac
+
+ swaplim=$(growfs_swap_max)
+
+ [ $verbose -eq 1 ] && {
+ echo "diskdev is $diskdev"
+ echo "search is $search"
+ echo "swapsize is $swapsize"
+ echo "swaplim is $swaplim"
+ }
+
+ sysctl -b kern.geom.conftxt | awk '
+{
+ verbose = 0
+ lvl=$1
+ device[lvl] = $3
+ type[lvl] = $2
+ idx[lvl] = $7
+ offset[lvl] = $9
+ parttype[lvl] = $13
+ size[lvl] = $4
+ if (verbose) print lvl, type[lvl], $3
+ if (type[lvl] == "DISK") {
+ disksize = size[lvl]
+ if (verbose)
+ print "disksize ", disksize
+ # Do not add swap on disks under 15 GB (decimal) by default.
+ if (addswap == 1 && (size[lvl] > 15000000000 || swapsize > 0))
+ doing_swap = 1
+ else
+ doing_swap = 0
+ } else if (type[lvl] == "PART" && $11 == "freebsd-swap" && \
+ int(swapsize) == 0) {
+ # This finds swap only if it precedes root, e.g. preceding disk.
+ addswap = 0
+ doing_swap = 0
+ print "swap device exists, not adding swap"
+ }
+ if (dev == $3) {
+ for (i = 1; i <= lvl; i++) {
+ # resize
+ if (type[i] == "PART") {
+ pdev = device[i - 1]
+ if (verbose)
+ print i, pdev, addswap, disksize, \
+ doing_swap
+ swapcmd = ""
+ # Allow swap if current root is < 40% of disk.
+ if (parttype[i] != "MBR" && doing_swap == 1 && \
+ (size[i] / disksize < 0.4 || \
+ swapsize > 0)) {
+ print "Adding swap partition"
+ if (int(swapsize) == 0) {
+ swapsize = int(disksize / 10)
+ if (swapsize > swaplim)
+ swapsize = swaplim
+ }
+ sector = $5
+ swapsize /= sector
+ if (verbose)
+ print "swapsize sectors",
+ swapsize
+ align = 4 * 1024 * 1024 / sector
+
+ # Estimate offset for swap; let
+ # gpart compute actual start and size.
+ # Assume expansion all goes into this
+ # partition for MBR case.
+ if (parttype[i - 1] == "MBR") {
+ if (verbose)
+ print "sz ", size[i - 1], \
+ " off ", offset[i - 1]
+ expand = size[0] - \
+ (size[i - 1] + offset[i - 1])
+ } else {
+ if (verbose)
+ print "sz ", size[i], \
+ " off ", offset[i]
+ expand = size[0] - \
+ (size[i] + offset[i])
+ }
+ if (verbose)
+ print "expand ", expand, \
+ " sz ", size[i]
+ swapbase = (expand + size[i]) / sector
+ swapbase -= swapsize + align
+ swapcmd = "gpart add -t freebsd-swap -a " align " -b " int(swapbase) " " pdev " && kenv growfs_swap_pdev=" pdev " >/dev/null; "
+ if (verbose)
+ swapcmd = "set -x; gpart show; " swapcmd
+ }
+ cmd[i] = swapcmd "gpart resize -i " idx[i] " " pdev
+ if (parttype[i] == "GPT")
+ cmd[i] = "gpart recover " pdev " ; " cmd[i]
+ } else if (type[i] == "LABEL") {
+ continue
+ } else {
+ print "unhandled type: " type[i]
+ exit 1
+ }
+ }
+ for (i = 1; i <= lvl; i++) {
+ if (cmd[i])
+ system(cmd[i])
+ }
+ exit 0
+ }
+}' dev="$search" addswap="$addswap" swapsize="$swapsize" swaplim="$swaplim"
+ gpart commit "$diskdev" 2> /dev/null
+ case "$FSTYPE" in
+ ufs)
+ growfs -y /dev/"$rootdev"
+ ;;
+ zfs)
+ zpool online -e $pool $rootdev
+ ;;
+ esac
+
+ # Get parent device of swap partition if one was added;
+ # if so, find swap device and label it.
+ pdev=$(kenv -q growfs_swap_pdev)
+ if [ -n "$pdev" ]
+ then
+ dev=$(growfs_last_swap "$pdev")
+ if [ -z "$dev" ]
+ then
+ echo "Swap partition not found on $pdev"
+ exit 0
+ fi
+ glabel label -v growfs_swap $dev
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+growfs_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/growfs_fstab b/libexec/rc/rc.d/growfs_fstab
new file mode 100755
index 000000000000..8b7cea3a63e5
--- /dev/null
+++ b/libexec/rc/rc.d/growfs_fstab
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+# Copyright 2022 Michael J. Karels
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: growfs_fstab
+# REQUIRE: growfs root
+# KEYWORD: firstboot
+
+# If the growfs script added a swap partition, then add a swap entry
+# to /etc/fstab if none exists, and add as dumpdev.
+
+. /etc/rc.subr
+
+name="growfs_fstab"
+desc="Add new swap partition to /etc/fstab"
+start_cmd="growfs_fstab_start"
+stop_cmd=":"
+rcvar="growfs_enable"
+
+growfs_fstab_start()
+{
+ if kenv -q growfs_swap_pdev >/dev/null
+ then
+ if awk '
+ /^#/ { next }
+ $3 == "swap" { exit 1 }
+ ' < /etc/fstab
+ then
+ printf "/dev/label/growfs_swap\tnone\t\tswap\tsw\t\t0\t0\n" >>/etc/fstab
+ printf '# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable\n' >>/etc/rc.conf
+ printf 'dumpdev="AUTO"\n' >>/etc/rc.conf
+ dumpon $dumpon_flags /dev/label/growfs_swap
+ fi
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+growfs_fstab_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/gssd b/libexec/rc/rc.d/gssd
new file mode 100755
index 000000000000..7ab3c181eeb1
--- /dev/null
+++ b/libexec/rc/rc.d/gssd
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: gssd
+# REQUIRE: root mountcritlocal NETWORKING kdc
+# BEFORE: mountcritremote
+# KEYWORD: nojailvnet shutdown
+
+. /etc/rc.subr
+
+name=gssd
+desc="Generic Security Services Daemon"
+rcvar=gssd_enable
+
+: ${gssd_svcj_options:="net_basic nfsd"}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/hastd b/libexec/rc/rc.d/hastd
new file mode 100755
index 000000000000..37df43d26c7d
--- /dev/null
+++ b/libexec/rc/rc.d/hastd
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: hastd
+# REQUIRE: NETWORKING syslogd
+# BEFORE: DAEMON
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="hastd"
+desc="Highly Available Storage daemon"
+rcvar="hastd_enable"
+pidfile="/var/run/${name}.pid"
+command="/sbin/${name}"
+hastctl="/sbin/hastctl"
+required_files="/etc/hast.conf"
+stop_precmd="hastd_stop_precmd"
+required_modules="geom_gate:g_gate"
+extra_commands="reload"
+
+hastd_stop_precmd()
+{
+ ${hastctl} role init all
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+hastd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/hcsecd b/libexec/rc/rc.d/hcsecd
new file mode 100755
index 000000000000..8827e53777f3
--- /dev/null
+++ b/libexec/rc/rc.d/hcsecd
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: hcsecd
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="hcsecd"
+desc="Control link keys and PIN codes for Bluetooth devices"
+rcvar="hcsecd_enable"
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+required_modules="ng_btsocket"
+
+load_rc_config $name
+config="${hcsecd_config:-/etc/bluetooth/${name}.conf}"
+command_args="-f ${config}"
+required_files="${config}"
+
+# doesn't make sense to run in a svcj: nojail keyword
+hcsecd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/hostapd b/libexec/rc/rc.d/hostapd
new file mode 100755
index 000000000000..15b20c95c488
--- /dev/null
+++ b/libexec/rc/rc.d/hostapd
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: hostapd
+# REQUIRE: mountcritremote
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="hostapd"
+desc="Authenticator for IEEE 802.11 networks"
+command=${hostapd_program}
+
+ifn="$2"
+if [ -z "$ifn" ]; then
+ rcvar="hostapd_enable"
+ conf_file="/etc/${name}.conf"
+ pidfile="/var/run/${name}.pid"
+else
+ rcvar=
+ conf_file="/etc/${name}-${ifn}.conf"
+ pidfile="/var/run/${name}-${ifn}.pid"
+fi
+
+command_args="-P ${pidfile} -B ${conf_file}"
+required_files="${conf_file}"
+required_modules="wlan_xauth wlan_wep wlan_tkip wlan_ccmp wlan_gcmp"
+extra_commands="reload"
+
+load_rc_config ${name}
+
+# doesn't make sense to run in a svcj: nojail keyword
+hostapd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/hostid b/libexec/rc/rc.d/hostid
new file mode 100755
index 000000000000..bde88d7e6be5
--- /dev/null
+++ b/libexec/rc/rc.d/hostid
@@ -0,0 +1,165 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+# Copyright (c) 2015 Xin LI <delphij@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: hostid
+# REQUIRE: sysctl
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="hostid"
+desc="Generate a unique host ID"
+start_cmd="hostid_start"
+stop_cmd=":"
+reset_cmd="hostid_reset"
+extra_commands="reset"
+rcvar="hostid_enable"
+
+hostid_set()
+{
+ uuid=$1
+ # Generate hostid based on hostuuid - take first four bytes from md5(uuid).
+ id=`echo -n $uuid | /sbin/md5`
+ id="0x${id%????????????????????????}"
+
+ # Set both kern.hostuuid and kern.hostid.
+ #
+ startmsg "Setting hostuuid: ${uuid}."
+ ${SYSCTL} kern.hostuuid="${uuid}" >/dev/null
+ startmsg "Setting hostid: ${id}."
+ ${SYSCTL} kern.hostid=${id} >/dev/null
+}
+
+valid_hostid()
+{
+ uuid=$1
+
+ x="[0-9a-f]"
+ y=$x$x$x$x
+
+ # Check against a blacklist before
+ # accepting the UUID.
+ case "${uuid}" in
+ 00000000-0000-0000-0000-000000000000)
+ ;;
+ 00020003-0004-0005-0006-000700080009)
+ ;;
+ 03000200-0400-0500-0006-000700080009)
+ ;;
+ 07090201-0103-0301-0807-060504030201)
+ ;;
+ 11111111-1111-1111-1111-111111111111)
+ ;;
+ 11111111-2222-3333-4444-555555555555)
+ ;;
+ 12345678-1234-5678-90ab-cddeefaabbcc)
+ ;;
+ 4c4c4544-0000-2010-8020-80c04f202020)
+ ;;
+ 58585858-5858-5858-5858-585858585858)
+ ;;
+ 890e2d14-cacd-45d1-ae66-bc80e8bfeb0f)
+ ;;
+ 8e275844-178f-44a8-aceb-a7d7e5178c63)
+ ;;
+ dc698397-fa54-4cf2-82c8-b1b5307a6a7f)
+ ;;
+ fefefefe-fefe-fefe-fefe-fefefefefefe)
+ ;;
+ *-ffff-ffff-ffff-ffffffffffff)
+ ;;
+ $y$y-$y-$y-$y-$y$y$y)
+ return 0
+ ;;
+ esac
+
+ return 1
+}
+
+hostid_hardware()
+{
+ uuid=`kenv -q smbios.system.uuid`
+
+ if valid_hostid $uuid; then
+ echo "${uuid}"
+ elif [ "$uuid" ]; then
+ echo "INVALID"
+ fi
+}
+
+hostid_generate()
+{
+ # First look for UUID in hardware.
+ uuid=`hostid_hardware`
+
+ # Warn about invalid UUIDs
+ if [ "${uuid}" = "INVALID" ]; then
+ warn "hostid: unable to figure out a UUID from DMI data, generating a new one"
+ sleep 2
+ uuid=""
+ fi
+
+ # Generate a random UUID if invalid or not found
+ if [ -z "${uuid}" ]; then
+ # If not found, fall back to software-generated UUID.
+ uuid=`uuidgen ${hostid_uuidgen_flags}`
+ fi
+ hostid_set $uuid
+}
+
+hostid_reset()
+{
+ hostid_generate
+ # Store newly generated UUID in ${hostid_file}.
+ echo $uuid > ${hostid_file}
+ if [ $? -ne 0 ]; then
+ warn "could not store hostuuid in ${hostid_file}."
+ fi
+}
+
+hostid_start()
+{
+ # If ${hostid_file} already exists, we take UUID from there.
+ if [ -r ${hostid_file} ]; then
+ read saved_hostid < ${hostid_file}
+ if valid_hostid ${saved_hostid}; then
+ hostid_set ${saved_hostid}
+ exit 0
+ fi
+ fi
+
+ # No hostid file, generate UUID.
+ hostid_generate
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+hostid_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/hostid_save b/libexec/rc/rc.d/hostid_save
new file mode 100755
index 000000000000..b9727d24bc57
--- /dev/null
+++ b/libexec/rc/rc.d/hostid_save
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: hostid_save
+# REQUIRE: hostid root
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="hostid_save"
+desc="Save unique host ID to disk"
+start_cmd="hostid_save"
+stop_cmd=":"
+rcvar="hostid_enable"
+
+hostid_machine_id()
+{
+ local IFS
+
+ IFS=-
+ set -- ${current_hostid}
+ IFS=
+ current_machine_id=$*
+}
+
+hostid_save()
+{
+ current_hostid=`$SYSCTL_N kern.hostuuid`
+
+ read saved_hostid 2>/dev/null < ${hostid_file}
+ if [ "${saved_hostid}" != "${current_hostid}" ]; then
+ echo "${current_hostid}" > ${hostid_file} ||
+ warn "could not store hostuuid in ${hostid_file}."
+ fi
+
+ hostid_machine_id
+
+ read saved_machine_id 2>/dev/null < ${machine_id_file}
+ if [ "${saved_machine_id}" != "${current_machine_id}" ]; then
+ echo "${current_machine_id}" > ${machine_id_file} ||
+ warn "could not store hostuuid in ${machine_id_file}."
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+hostid_save_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/hostname b/libexec/rc/rc.d/hostname
new file mode 100755
index 000000000000..0bc31ccd787e
--- /dev/null
+++ b/libexec/rc/rc.d/hostname
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 The FreeBSD Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: hostname
+# REQUIRE: FILESYSTEMS
+# BEFORE: netif
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="hostname"
+desc="Set the system\'s hostname"
+start_cmd="hostname_start"
+stop_cmd=":"
+
+hostname_start()
+{
+ # If we are not inside a jail, set the host name.
+ # If we are inside a jail, set the host name if it is permitted.
+ #
+ if check_jail jailed; then
+ if ! check_jail set_hostname_allowed; then
+ return
+ fi
+ else
+ # If we're not in a jail and rc.conf doesn't specify a
+ # hostname, see if we can get one from kenv.
+ #
+ if [ -z "${hostname}" -a \
+ -n "`/bin/kenv dhcp.host-name 2> /dev/null`" ]; then
+ hostname=`/bin/kenv dhcp.host-name`
+ fi
+ fi
+
+ # Have we got a hostname yet?
+ #
+ if [ -z "${hostname}" ]; then
+ # Null hostname is probably OK if DHCP is in use,
+ # or when hostname is already set (common for jails).
+ #
+ if [ -z "`list_net_interfaces dhcp`" -a \
+ -z "`/bin/hostname`" ]; then
+ warn "\$hostname is not set -- see rc.conf(5)."
+ fi
+ return
+ fi
+
+ # All right, it is safe to invoke hostname(1) now.
+ #
+ startmsg -n "Setting hostname: ${hostname}"
+ /bin/hostname "${hostname}"
+ startmsg '.'
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+hostname_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/inetd b/libexec/rc/rc.d/inetd
new file mode 100755
index 000000000000..81cc18d95be2
--- /dev/null
+++ b/libexec/rc/rc.d/inetd
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: inetd
+# REQUIRE: DAEMON LOGIN FILESYSTEMS
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="inetd"
+desc="Internet \"super-server\""
+rcvar="inetd_enable"
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+required_files="/etc/${name}.conf"
+extra_commands="reload"
+
+: ${inetd_svcj_options:="net_basic"}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/iovctl b/libexec/rc/rc.d/iovctl
new file mode 100755
index 000000000000..70dc783aafb0
--- /dev/null
+++ b/libexec/rc/rc.d/iovctl
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: iovctl
+# REQUIRE: FILESYSTEMS sysctl kld
+
+. /etc/rc.subr
+
+name="iovctl"
+command="/usr/sbin/iovctl"
+start_cmd="iovctl_start"
+stop_cmd="iovctl_stop"
+
+run_iovctl()
+{
+ local _f flag
+
+ flag=$1
+ for _f in ${iovctl_files} ; do
+ if [ -r ${_f} ]; then
+ ${command} ${flag} -f ${_f} > /dev/null
+ fi
+ done
+}
+
+iovctl_start()
+{
+ run_iovctl -C
+}
+
+iovctl_stop()
+{
+ run_iovctl -D
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+iovctl_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ip6addrctl b/libexec/rc/rc.d/ip6addrctl
new file mode 100755
index 000000000000..eac1d2729e78
--- /dev/null
+++ b/libexec/rc/rc.d/ip6addrctl
@@ -0,0 +1,127 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ip6addrctl
+# REQUIRE: FILESYSTEMS
+# BEFORE: netif
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="ip6addrctl"
+desc="configure address selection policy for IPv6 and IPv4"
+rcvar="ip6addrctl_enable"
+start_cmd="ip6addrctl_start"
+stop_cmd="ip6addrctl_stop"
+extra_commands="status prefer_ipv6 prefer_ipv4"
+status_cmd="ip6addrctl"
+prefer_ipv6_cmd="ip6addrctl_prefer_ipv6"
+prefer_ipv4_cmd="ip6addrctl_prefer_ipv4"
+config_file="/etc/ip6addrctl.conf"
+
+set_rcvar_obsolete ipv6_enable ipv6_activate_all_interfaces
+set_rcvar_obsolete ipv6_prefer ip6addrctl_policy
+
+IP6ADDRCTL_CMD="/usr/sbin/ip6addrctl"
+
+ip6addrctl_prefer_ipv6()
+{
+ afexists inet6 || return 0
+
+ ${IP6ADDRCTL_CMD} flush >/dev/null 2>&1
+ cat <<EOT | ${IP6ADDRCTL_CMD} install /dev/stdin
+ ::1/128 50 0
+ ::/0 40 1
+ ::ffff:0:0/96 35 4
+ 2002::/16 30 2
+ 2001::/32 5 5
+ fc00::/7 3 13
+ ::/96 1 3
+ fec0::/10 1 11
+ 3ffe::/16 1 12
+EOT
+}
+
+ip6addrctl_prefer_ipv4()
+{
+ afexists inet6 || return 0
+
+ ${IP6ADDRCTL_CMD} flush >/dev/null 2>&1
+ cat <<EOT | ${IP6ADDRCTL_CMD} install /dev/stdin
+ ::1/128 50 0
+ ::/0 40 1
+ ::ffff:0:0/96 100 4
+ 2002::/16 30 2
+ 2001::/32 5 5
+ fc00::/7 3 13
+ ::/96 1 3
+ fec0::/10 1 11
+ 3ffe::/16 1 12
+EOT
+}
+
+ip6addrctl_start()
+{
+ afexists inet6 || return 0
+
+ # install the policy of the address selection algorithm.
+ case "${ip6addrctl_policy}" in
+ [Aa][Uu][Tt][Oo])
+ if [ -r "${config_file}" -a -s "${config_file}" ]; then
+ ${IP6ADDRCTL_CMD} flush >/dev/null 2>&1
+ ${IP6ADDRCTL_CMD} install "${config_file}"
+ else
+ if checkyesno ipv6_activate_all_interfaces; then
+ ip6addrctl_prefer_ipv6
+ elif [ -n "$(list_vars ifconfig_\*_ipv6)" ]; then
+ ip6addrctl_prefer_ipv6
+ else
+ ip6addrctl_prefer_ipv4
+ fi
+ fi
+ ;;
+ ipv4_prefer)
+ ip6addrctl_prefer_ipv4
+ ;;
+ ipv6_prefer)
+ ip6addrctl_prefer_ipv6
+ ;;
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+ # Backward compatibility when ipv6_prefer=YES
+ ip6addrctl_prefer_ipv6
+ ;;
+ [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
+ # Backward compatibility when ipv6_prefer=NO
+ ip6addrctl_prefer_ipv4
+ ;;
+ [Nn][Oo][Nn][Ee])
+ ${IP6ADDRCTL_CMD} flush >/dev/null 2>&1
+ ;;
+ *)
+ warn "\$ip6addrctl_policy is invalid: ${ip6addrctl_policy}. " \
+ " \"ipv4_prefer\" is used instead."
+ ip6addrctl_prefer_ipv4
+ ;;
+ esac
+
+ if checkyesno ip6addrctl_verbose; then
+ echo 'Address selection policy table for IPv4 and IPv6:'
+ ${IP6ADDRCTL_CMD}
+ fi
+}
+
+ip6addrctl_stop()
+{
+ afexists inet6 || return 0
+
+ ip6addrctl flush >/dev/null 2>&1
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+ipv6addrctl_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ipfilter b/libexec/rc/rc.d/ipfilter
new file mode 100755
index 000000000000..9b64fcff0c7a
--- /dev/null
+++ b/libexec/rc/rc.d/ipfilter
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ipfilter
+# REQUIRE: FILESYSTEMS
+# BEFORE: ipmon ipnat netif netwait securelevel
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="ipfilter"
+desc="IP packet filter"
+rcvar="ipfilter_enable"
+load_rc_config $name
+stop_precmd="test -f ${ipfilter_rules}"
+
+# doesn't make sense to run in a svcj: config setting
+ipfilter_svcj="NO"
+
+start_precmd="$stop_precmd"
+start_cmd="ipfilter_start"
+stop_cmd="ipfilter_stop"
+reload_precmd="$stop_precmd"
+reload_cmd="ipfilter_reload"
+resync_precmd="$stop_precmd"
+resync_cmd="ipfilter_resync"
+status_precmd="$stop_precmd"
+status_cmd="ipfilter_status"
+extra_commands="reload resync"
+required_modules="ipl:ipfilter"
+
+ipfilter_start()
+{
+ echo "Enabling ipfilter."
+ if [ -n "${ifilter_optionlist}" ]; then
+ if ${ipfilter_program:-/sbin/ipf} -V | grep -q 'Running: yes'; then
+ ${ipfilter_program:-/sbin/ipf} -D
+ fi
+ ${ipfilter_program:-/sbin/ipf} -T "${ipfilter_optionlist}"
+ ${ipfilter_program:-/sbin/ipf} -E
+ elif ! ${ipfilter_program:-/sbin/ipf} -V | grep -q 'Running: yes'; then
+ ${ipfilter_program:-/sbin/ipf} -E
+ fi
+ ${ipfilter_program:-/sbin/ipf} -Fa
+ if [ -r "${ipfilter_rules}" ]; then
+ ${ipfilter_program:-/sbin/ipf} \
+ -f "${ipfilter_rules}" ${ipfilter_flags}
+ fi
+}
+
+ipfilter_stop()
+{
+ if ${ipfilter_program:-/sbin/ipf} -V | grep -q 'Running: yes'; then
+ echo "Saving firewall state tables"
+ ${ipfs_program:-/sbin/ipfs} -W ${ipfs_flags}
+ echo "Disabling ipfilter."
+ ${ipfilter_program:-/sbin/ipf} -D
+ fi
+}
+
+ipfilter_reload()
+{
+ echo "Reloading ipfilter rules."
+
+ ${ipfilter_program:-/sbin/ipf} -I -Fa
+ if [ -r "${ipfilter_rules}" ]; then
+ ${ipfilter_program:-/sbin/ipf} -I \
+ -f "${ipfilter_rules}" ${ipfilter_flags}
+ if [ $? -ne 0 ]; then
+ err 1 'Load of rules into alternate set failed; aborting reload'
+ fi
+ fi
+ ${ipfilter_program:-/sbin/ipf} -s
+
+}
+
+ipfilter_resync()
+{
+ ${ipfilter_program:-/sbin/ipf} -y ${ipfilter_flags}
+}
+
+ipfilter_status()
+{
+ ${ipfilter_program:-/sbin/ipf} -V
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ipfs b/libexec/rc/rc.d/ipfs
new file mode 100755
index 000000000000..2ec4ad3b1d00
--- /dev/null
+++ b/libexec/rc/rc.d/ipfs
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ipfs
+# REQUIRE: ipnat
+# BEFORE: netif
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="ipfs"
+desc="Saves and restores information for NAT and state tables"
+rcvar="ipfs_enable"
+start_cmd="ipfs_start"
+stop_cmd="ipfs_stop"
+start_precmd="ipfs_prestart"
+
+ipfs_prestart()
+{
+ # Do not continue if either ipnat or ipfilter is not enabled or
+ # if the ipfilter module is not loaded.
+ #
+ if ! checkyesno ipfilter_enable -o ! checkyesno ipnat_enable ; then
+ err 1 "${name} requires either ipfilter or ipnat enabled"
+ fi
+ if ! ${ipfilter_program:-/sbin/ipf} -V | grep -q 'Running: yes' >/dev/null 2>&1; then
+ err 1 "ipfilter module is not loaded"
+ fi
+ return 0
+}
+
+ipfs_start()
+{
+ if [ -r /var/db/ipf/ipstate.ipf -a -r /var/db/ipf/ipnat.ipf ]; then
+ ${ipfs_program} -R ${rc_flags}
+ rm -f /var/db/ipf/ipstate.ipf /var/db/ipf/ipnat.ipf
+ fi
+}
+
+ipfs_stop()
+{
+ if [ ! -d /var/db/ipf ]; then
+ mkdir /var/db/ipf
+ chmod 700 /var/db/ipf
+ chown root:wheel /var/db/ipf
+ fi
+ ${ipfs_program} -W ${rc_flags}
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+ipfs_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ipfw b/libexec/rc/rc.d/ipfw
new file mode 100755
index 000000000000..6d6f7577828f
--- /dev/null
+++ b/libexec/rc/rc.d/ipfw
@@ -0,0 +1,169 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ipfw
+# REQUIRE: ppp
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="ipfw"
+desc="Firewall, traffic shaper, packet scheduler, in-kernel NAT"
+rcvar="firewall_enable"
+start_cmd="ipfw_start"
+start_precmd="ipfw_prestart"
+start_postcmd="ipfw_poststart"
+stop_cmd="ipfw_stop"
+status_cmd="ipfw_status"
+required_modules="ipfw"
+extra_commands="status"
+
+set_rcvar_obsolete ipv6_firewall_enable
+
+ipfw_prestart()
+{
+ if checkyesno dummynet_enable; then
+ required_modules="$required_modules dummynet"
+ fi
+ if checkyesno natd_enable; then
+ required_modules="$required_modules ipdivert"
+ fi
+ if checkyesno firewall_nat_enable; then
+ required_modules="$required_modules ipfw_nat"
+ fi
+ if checkyesno firewall_nat64_enable; then
+ required_modules="$required_modules ipfw_nat64"
+ fi
+ if checkyesno firewall_nptv6_enable; then
+ required_modules="$required_modules ipfw_nptv6"
+ fi
+ if checkyesno firewall_pmod_enable; then
+ required_modules="$required_modules ipfw_pmod"
+ fi
+}
+
+ipfw_start()
+{
+ local _firewall_type _module _sysctl_reload
+
+ if [ -n "${1}" ]; then
+ _firewall_type=$1
+ else
+ _firewall_type=${firewall_type}
+ fi
+
+ _sysctl_reload=no
+ for _module in ${required_modules}
+ do
+ if kldstat -qn ${_module}; then
+ _sysctl_reload=yes
+ break
+ fi
+ done
+
+ if [ ${_sysctl_reload} = yes ]; then
+ /etc/rc.d/sysctl reload
+ fi
+
+ # set the firewall rules script if none was specified
+ [ -z "${firewall_script}" ] && firewall_script=/etc/rc.firewall
+
+ if [ -r "${firewall_script}" ]; then
+ /bin/sh "${firewall_script}" "${_firewall_type}"
+ echo 'Firewall rules loaded.'
+ elif [ "`ipfw list 65535`" = "65535 deny ip from any to any" ]; then
+ echo 'Warning: kernel has firewall functionality, but' \
+ 'firewall rules are not enabled.'
+ echo ' All ip services are disabled.'
+ fi
+
+ # Firewall logging
+ #
+ if checkyesno firewall_logging; then
+ echo 'Firewall logging enabled.'
+ ${SYSCTL} net.inet.ip.fw.verbose=1 >/dev/null
+ fi
+ if checkyesno firewall_logif; then
+ if ! ifconfig ipfw0 >/dev/null 2>&1; then
+ ifconfig ipfw0 create
+ echo 'Firewall logging pseudo-interface (ipfw0)' \
+ 'created.'
+ else
+ echo 'Firewall logging pseudo-interface (ipfw0)' \
+ 'already created.'
+ fi
+ fi
+}
+
+ipfw_poststart()
+{
+ local _coscript
+
+ # Start firewall coscripts
+ #
+ for _coscript in ${firewall_coscripts} ; do
+ if [ -f "${_coscript}" ]; then
+ ${_coscript} quietstart
+ fi
+ done
+
+ # Enable the firewall
+ #
+ if ! ${SYSCTL} net.inet.ip.fw.enable=1 >/dev/null 2>&1; then
+ warn "failed to enable IPv4 firewall"
+ fi
+ if afexists inet6; then
+ if ! ${SYSCTL} net.inet6.ip6.fw.enable=1 >/dev/null 2>&1
+ then
+ warn "failed to enable IPv6 firewall"
+ fi
+ fi
+}
+
+ipfw_stop()
+{
+ local _coscript
+
+ # Disable the firewall
+ #
+ ${SYSCTL} net.inet.ip.fw.enable=0 >/dev/null
+ if afexists inet6; then
+ ${SYSCTL} net.inet6.ip6.fw.enable=0 >/dev/null
+ fi
+
+ # Stop firewall coscripts
+ #
+ for _coscript in `reverse_list ${firewall_coscripts}` ; do
+ if [ -f "${_coscript}" ]; then
+ ${_coscript} quietstop
+ fi
+ done
+}
+
+ipfw_status()
+{
+ status=$(sysctl -i -n net.inet.ip.fw.enable)
+ : ${status:=0}
+ if afexists inet6; then
+ status6=$(sysctl -i -n net.inet6.ip6.fw.enable)
+ : ${status6:=0}
+ status=$((${status} + ${status6}))
+ fi
+ if [ ${status} -eq 0 ]; then
+ echo "ipfw is not enabled"
+ exit 1
+ else
+ echo "ipfw is enabled"
+ exit 0
+ fi
+}
+
+load_rc_config $name
+firewall_coscripts="/etc/rc.d/natd ${firewall_coscripts}"
+
+# doesn't make sense to run in a svcj: config setting
+ipfw_svcj="NO"
+
+run_rc_command $*
diff --git a/libexec/rc/rc.d/ipfw_netflow b/libexec/rc/rc.d/ipfw_netflow
new file mode 100755
index 000000000000..129488ce60d0
--- /dev/null
+++ b/libexec/rc/rc.d/ipfw_netflow
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ipfw_netflow
+# REQUIRE: ipfw
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="ipfw_netflow"
+desc="firewall, ipfw, netflow"
+rcvar="${name}_enable"
+start_cmd="${name}_start"
+stop_cmd="${name}_stop"
+start_precmd="${name}_test"
+status_cmd="${name}_status"
+required_modules="ipfw ng_netflow ng_ipfw"
+extra_commands="status"
+
+: ${ipfw_netflow_hook:=9995}
+: ${ipfw_netflow_rule:=01000}
+: ${ipfw_netflow_ip:=127.0.0.1}
+: ${ipfw_netflow_port:=9995}
+: ${ipfw_netflow_version:=}
+
+ipfw_netflow_test()
+{
+ if [ "${ipfw_netflow_version}" != "" ] && [ "${ipfw_netflow_version}" != 9 ]; then
+ err 1 "Unknown netflow version \'${ipfw_netflow_version}\'"
+ fi
+ case "${ipfw_netflow_hook}" in
+ [!0-9]*)
+ err 1 "Bad value \"${ipfw_netflow_hook}\": Hook must be numerical"
+ esac
+ case "${ipfw_netflow_rule}" in
+ [!0-9]*)
+ err 1 "Bad value \"${ipfw_netflow_rule}\": Rule number must be numerical"
+ esac
+}
+
+ipfw_netflow_is_running()
+{
+ ngctl show netflow: > /dev/null 2>&1 && return 0 || return 1
+}
+
+ipfw_netflow_status()
+{
+ ipfw_netflow_is_running && echo "ipfw_netflow is active" || echo "ipfw_netflow is not active"
+}
+
+ipfw_netflow_start()
+{
+ ipfw_netflow_is_running && err 1 "ipfw_netflow is already active"
+ ipfw add ${ipfw_netflow_rule} ngtee ${ipfw_netflow_hook} ip from any to any ${ipfw_netflow_fib:+fib ${ipfw_netflow_fib}}
+ ngctl -f - <<-EOF
+ mkpeer ipfw: netflow ${ipfw_netflow_hook} iface0
+ name ipfw:${ipfw_netflow_hook} netflow
+ mkpeer netflow: ksocket export${ipfw_netflow_version} inet/dgram/udp
+ msg netflow: setdlt {iface=0 dlt=12}
+ name netflow:export${ipfw_netflow_version} netflow_export
+ msg netflow:export${ipfw_netflow_version} connect inet/${ipfw_netflow_ip}:${ipfw_netflow_port}
+EOF
+}
+
+ipfw_netflow_stop()
+{
+ ipfw_netflow_is_running || err 1 "ipfw_netflow is not active"
+ ngctl shutdown netflow:
+ ipfw delete ${ipfw_netflow_rule}
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+ipfw_netflow_svcj="NO"
+
+run_rc_command $*
diff --git a/libexec/rc/rc.d/ipmon b/libexec/rc/rc.d/ipmon
new file mode 100755
index 000000000000..3ef0c895ad16
--- /dev/null
+++ b/libexec/rc/rc.d/ipmon
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ipmon
+# REQUIRE: FILESYSTEMS hostname sysctl
+# BEFORE: SERVERS
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="ipmon"
+desc="Monitors /dev/ipl for logged packets"
+rcvar="ipmon_enable"
+command="/sbin/${name}"
+start_precmd="ipmon_precmd"
+
+# no svcj options needed
+: ${ipmon_svcj_options:=""}
+
+ipmon_precmd()
+{
+ # Continue only if ipfilter or ipnat is enabled and the
+ # ipfilter module is loaded.
+ #
+ if ! checkyesno ipfilter_enable && ! checkyesno ipnat_enable && ! checkyesno rc_force ; then
+ err 1 "${name} requires either ipfilter or ipnat enabled"
+ fi
+ if ! ${ipfilter_program:-/sbin/ipf} -V | grep -q 'Running: yes' >/dev/null 2>&1; then
+ err 1 "ipfilter module is not loaded"
+ fi
+ return 0
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ipnat b/libexec/rc/rc.d/ipnat
new file mode 100755
index 000000000000..56fe443686b1
--- /dev/null
+++ b/libexec/rc/rc.d/ipnat
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ipnat
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="ipnat"
+desc="user interface to the NAT subsystem"
+rcvar="ipnat_enable"
+load_rc_config $name
+start_cmd="ipnat_start"
+stop_cmd="${ipnat_program} -F -C"
+reload_cmd="${ipnat_program} -F -C -f ${ipnat_rules}"
+extra_commands="reload"
+required_files="${ipnat_rules}"
+required_modules="ipl:ipfilter"
+
+# doesn't make sense to run in a svcj: config setting
+ipnat_svcj="NO"
+
+ipnat_start()
+{
+ echo "Installing NAT rules."
+ ${ipnat_program} -CF -f ${ipnat_rules} ${ipnat_flags}
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ippool b/libexec/rc/rc.d/ippool
new file mode 100755
index 000000000000..0db8bbe98f61
--- /dev/null
+++ b/libexec/rc/rc.d/ippool
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ippool
+# REQUIRE: FILESYSTEMS
+# BEFORE: ipfilter
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="ippool"
+desc="user interface to the IPFilter pools"
+rcvar="ippool_enable"
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+ippool_svcj="NO"
+
+start_precmd="ippool_start_precmd"
+stop_cmd="${ippool_program} -F"
+reload_cmd="ippool_reload"
+extra_commands="reload"
+required_files="${ippool_rules}"
+required_modules="ipl:ipfilter"
+
+ippool_start_precmd()
+{
+ rc_flags="-f ${ippool_rules} ${rc_flags}"
+}
+
+ippool_reload()
+{
+ echo "Reloading IP Pools."
+ ${stop_cmd}
+ ${start_cmd}
+}
+
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ipropd_master b/libexec/rc/rc.d/ipropd_master
new file mode 100755
index 000000000000..a3ca498afe6c
--- /dev/null
+++ b/libexec/rc/rc.d/ipropd_master
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ipropd_master
+# REQUIRE: kdc
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name=ipropd_master
+rcvar=${name}_enable
+required_files="$ipropd_master_keytab"
+start_precmd=${name}_start_precmd
+start_postcmd=${name}_start_postcmd
+
+: ${ipropd_master_svcj_options:="net_basic"}
+
+ipropd_master_start_precmd()
+{
+
+ if [ -z "$ipropd_master_slaves" ]; then
+ warn "\$ipropd_master_slaves is empty."
+ return 1
+ fi
+ for _slave in $ipropd_master_slaves; do
+ echo $_slave
+ done > /var/heimdal/slaves || return 1
+}
+ipropd_master_start_postcmd()
+{
+
+ echo "${name}: slave nodes: $ipropd_master_slaves"
+}
+
+load_rc_config $name
+
+command_args="$command_args \
+ --keytab=\"$ipropd_master_keytab\" \
+ --detach \
+"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ipropd_slave b/libexec/rc/rc.d/ipropd_slave
new file mode 100755
index 000000000000..1735cff3de86
--- /dev/null
+++ b/libexec/rc/rc.d/ipropd_slave
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ipropd_slave
+# REQUIRE: kdc
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name=ipropd_slave
+rcvar=${name}_enable
+required_files="$ipropd_slave_keytab"
+start_precmd=${name}_start_precmd
+
+: ${ipropd_slave_svcj_options:="net_basic"}
+
+ipropd_slave_start_precmd()
+{
+
+ if [ -z "$ipropd_slave_master" ]; then
+ warn "\$ipropd_slave_master is empty."
+ return 1
+ fi
+}
+
+load_rc_config $name
+
+command_args=" \
+ command_args \
+ --keytab=\"$ipropd_slave_keytab\" \
+ --detach \
+ $ipropd_slave_master"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ipsec b/libexec/rc/rc.d/ipsec
new file mode 100755
index 000000000000..0e7ad213ce67
--- /dev/null
+++ b/libexec/rc/rc.d/ipsec
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ipsec
+# REQUIRE: FILESYSTEMS
+# BEFORE: DAEMON mountcritremote
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="ipsec"
+desc="Internet Protocol Security protocol"
+rcvar="ipsec_enable"
+start_precmd="ipsec_prestart"
+start_cmd="ipsec_start"
+stop_precmd="test -f $ipsec_file"
+stop_cmd="ipsec_stop"
+reload_cmd="ipsec_reload"
+extra_commands="reload"
+ipsec_program="/sbin/setkey"
+required_modules="ipsec"
+# ipsec_file is set by rc.conf
+
+ipsec_prestart()
+{
+ if [ ! -f "$ipsec_file" ]; then
+ warn "$ipsec_file not readable; ipsec start aborted."
+ stop_boot
+ return 1
+ fi
+ return 0
+}
+
+ipsec_start()
+{
+ echo "Installing ipsec manual keys/policies."
+ ${ipsec_program} -f $ipsec_file
+}
+
+ipsec_stop()
+{
+ echo "Clearing ipsec manual keys/policies."
+
+ # Still not 100% sure if we would like to do this.
+ # It is very questionable to do this during shutdown session
+ # since it can hang any of the remaining IPv4/v6 sessions.
+ #
+ ${ipsec_program} -F
+ ${ipsec_program} -FP
+}
+
+ipsec_reload()
+{
+ echo "Reloading ipsec manual keys/policies."
+ ${ipsec_program} -f "$ipsec_file"
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+ipsec_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/iscsictl b/libexec/rc/rc.d/iscsictl
new file mode 100755
index 000000000000..247954e0d4f1
--- /dev/null
+++ b/libexec/rc/rc.d/iscsictl
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: iscsictl
+# REQUIRE: NETWORKING iscsid
+# BEFORE: DAEMON
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="iscsictl"
+desc="iSCSI initiator management utility"
+rcvar="iscsictl_enable"
+command="/usr/bin/${name}"
+command_args="${iscsictl_flags}"
+required_modules="iscsi"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+iscsictl_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/iscsid b/libexec/rc/rc.d/iscsid
new file mode 100755
index 000000000000..e2418e8baaa1
--- /dev/null
+++ b/libexec/rc/rc.d/iscsid
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: iscsid
+# REQUIRE: NETWORKING
+# BEFORE: DAEMON
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="iscsid"
+desc="iSCSI initiator daemon"
+rcvar="iscsid_enable"
+pidfile="/var/run/${name}.pid"
+command="/usr/sbin/${name}"
+required_modules="iscsi"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+iscsid_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/jail b/libexec/rc/rc.d/jail
new file mode 100755
index 000000000000..f059363e1e8d
--- /dev/null
+++ b/libexec/rc/rc.d/jail
@@ -0,0 +1,616 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: jail
+# REQUIRE: LOGIN FILESYSTEMS
+# BEFORE: securelevel
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="jail"
+desc="Manage system jails"
+rcvar="jail_enable"
+
+start_cmd="jail_start"
+start_postcmd="jail_warn"
+stop_cmd="jail_stop"
+config_cmd="jail_config"
+console_cmd="jail_console"
+status_cmd="jail_status"
+extra_commands="config console status"
+: ${jail_program:=/usr/sbin/jail}
+: ${jail_consolecmd:=/usr/bin/login -f root}
+: ${jail_jexec:=/usr/sbin/jexec}
+: ${jail_jls:=/usr/sbin/jls}
+
+need_dad_wait=
+
+# extract_var jv name param num defval
+# Extract value from ${jail_$jv_$name} or ${jail_$name} and
+# set it to $param. If not defined, $defval is used.
+# When $num is [0-9]*, ${jail_$jv_$name$num} are looked up and
+# $param is set by using +=. $num=0 is optional (params may start at 1).
+# When $num is YN or NY, the value is interpreted as boolean.
+# When $num is @, the value is interpreted as an array separted by IFS.
+extract_var()
+{
+ local i _jv _name _param _num _def _name1 _name2
+ _jv=$1
+ _name=$2
+ _param=$3
+ _num=$4
+ _def=$5
+
+ case $_num in
+ YN)
+ _name1=jail_${_jv}_${_name}
+ _name2=jail_${_name}
+ eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\"
+ if checkyesno $_name1; then
+ echo " $_param = 1;"
+ else
+ echo " $_param = 0;"
+ fi
+ ;;
+ NY)
+ _name1=jail_${_jv}_${_name}
+ _name2=jail_${_name}
+ eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\"
+ if checkyesno $_name1; then
+ echo " $_param = 0;"
+ else
+ echo " $_param = 1;"
+ fi
+ ;;
+ [0-9]*)
+ i=$_num
+ while : ; do
+ _name1=jail_${_jv}_${_name}${i}
+ _name2=jail_${_name}${i}
+ eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
+ if [ -n "$_tmpargs" ]; then
+ echo " $_param += \"$_tmpargs\";"
+ elif [ $i != 0 ]; then
+ break;
+ fi
+ i=$(($i + 1))
+ done
+ ;;
+ @)
+ _name1=jail_${_jv}_${_name}
+ _name2=jail_${_name}
+ eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
+ set -- $_tmpargs
+ if [ $# -gt 0 ]; then
+ echo -n " $_param = "
+ while [ $# -gt 1 ]; do
+ echo -n "\"$1\", "
+ shift
+ done
+ echo "\"$1\";"
+ fi
+ ;;
+ *)
+ _name1=jail_${_jv}_${_name}
+ _name2=jail_${_name}
+ eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
+ if [ -n "$_tmpargs" ]; then
+ echo " $_param = \"$_tmpargs\";"
+ fi
+ ;;
+ esac
+}
+
+# parse_options _j _jv
+# Parse options and create a temporary configuration file if necessary.
+#
+parse_options()
+{
+ local _j _jv _p
+ _j=$1
+ _jv=$2
+
+ _confwarn=0
+ if [ -z "$_j" ]; then
+ warn "parse_options: you must specify a jail"
+ return
+ fi
+ eval _jconf=\"\${jail_${_jv}_conf:-/etc/jail.${_j}.conf}\"
+ eval _rootdir=\"\$jail_${_jv}_rootdir\"
+ eval _jconfdir=\"/etc/jail.conf.d/${_j}.conf\"
+ eval _hostname=\"\$jail_${_jv}_hostname\"
+ if [ -z "$_rootdir" -o \
+ -z "$_hostname" ]; then
+ if [ -r "$_jconf" ]; then
+ _conf="$_jconf"
+ return 0
+ elif [ -r "$_jconfdir" ] && ! egrep -q \
+ '^\s*\.include\s*["'\'']?/etc/jail.conf.d/' "$jail_conf" \
+ 2>/dev/null; then
+ _conf="$_jconfdir"
+ return 0
+ elif [ -r "$jail_conf" ]; then
+ _conf="$jail_conf"
+ return 0
+ else
+ warn "Invalid configuration for $_j " \
+ "(no jail.conf, no hostname, or no path). " \
+ "Jail $_j was ignored."
+ fi
+ return 1
+ fi
+ eval _ip=\"\$jail_${_jv}_ip\"
+ if [ -z "$_ip" ] && ! check_kern_features vimage; then
+ warn "no ipaddress specified and no vimage support. " \
+ "Jail $_j was ignored."
+ return 1
+ fi
+ _conf=/var/run/jail.${_j}.conf
+ #
+ # To relieve confusion, show a warning message.
+ #
+ : ${jail_confwarn:=YES}
+ checkyesno jail_confwarn && _confwarn=1
+ if [ -r "$jail_conf" -o -r "$_jconf" ]; then
+ if ! checkyesno jail_parallel_start; then
+ warn "$_conf is created and used for jail $_j."
+ fi
+ fi
+ /usr/bin/install -m 0644 -o root -g wheel /dev/null $_conf || return 1
+
+ eval : \${jail_${_jv}_flags:=${jail_flags}}
+ eval _exec=\"\$jail_${_jv}_exec\"
+ eval _exec_start=\"\$jail_${_jv}_exec_start\"
+ eval _exec_stop=\"\$jail_${_jv}_exec_stop\"
+ if [ -n "${_exec}" ]; then
+ # simple/backward-compatible execution
+ _exec_start="${_exec}"
+ _exec_stop=""
+ else
+ # flexible execution
+ if [ -z "${_exec_start}" ]; then
+ _exec_start="/bin/sh /etc/rc"
+ if [ -z "${_exec_stop}" ]; then
+ _exec_stop="/bin/sh /etc/rc.shutdown jail"
+ fi
+ fi
+ fi
+ eval _interface=\"\${jail_${_jv}_interface:-${jail_interface}}\"
+ eval _parameters=\"\${jail_${_jv}_parameters:-${jail_parameters}}\"
+ eval _fstab=\"\${jail_${_jv}_fstab:-${jail_fstab:-/etc/fstab.$_j}}\"
+ (
+ date +"# Generated by rc.d/jail at %Y-%m-%d %H:%M:%S"
+ echo "$_j {"
+ extract_var $_jv hostname host.hostname - ""
+ extract_var $_jv rootdir path - ""
+ if [ -n "$_ip" ]; then
+ extract_var $_jv interface interface - ""
+ jail_handle_ips_option $_ip $_interface
+ alias=0
+ while : ; do
+ eval _x=\"\$jail_${_jv}_ip_multi${alias}\"
+ [ -z "$_x" ] && break
+
+ jail_handle_ips_option $_x $_interface
+ alias=$(($alias + 1))
+ done
+ case $need_dad_wait in
+ 1)
+ # Sleep to let DAD complete before
+ # starting services.
+ echo " exec.start += \"sleep " \
+ $(($(${SYSCTL_N} net.inet6.ip6.dad_count) + 1)) \
+ "\";"
+ ;;
+ esac
+ # These are applicable only to non-vimage jails.
+ extract_var $_jv fib exec.fib - ""
+ extract_var $_jv socket_unixiproute_only \
+ allow.raw_sockets NY YES
+ else
+ echo " vnet;"
+ extract_var $_jv vnet_interface vnet.interface @ ""
+ fi
+
+ echo " exec.clean;"
+ echo " exec.system_user = \"root\";"
+ echo " exec.jail_user = \"root\";"
+ extract_var $_jv exec_prestart exec.prestart 0 ""
+ extract_var $_jv exec_poststart exec.poststart 0 ""
+ extract_var $_jv exec_prestop exec.prestop 0 ""
+ extract_var $_jv exec_poststop exec.poststop 0 ""
+
+ echo " exec.start += \"$_exec_start\";"
+ extract_var $_jv exec_afterstart exec.start 0 ""
+ echo " exec.stop = \"$_exec_stop\";"
+
+ extract_var $_jv consolelog exec.consolelog - \
+ /var/log/jail_${_j}_console.log
+
+ if [ -r $_fstab ]; then
+ echo " mount.fstab = \"$_fstab\";"
+ fi
+
+ eval : \${jail_${_jv}_devfs_enable:=${jail_devfs_enable:-NO}}
+ if checkyesno jail_${_jv}_devfs_enable; then
+ echo " mount.devfs;"
+ eval _ruleset=\${jail_${_jv}_devfs_ruleset:-${jail_devfs_ruleset}}
+ case $_ruleset in
+ "") ;;
+ [0-9]*) echo " devfs_ruleset = \"$_ruleset\";" ;;
+ devfsrules_jail)
+ # XXX: This is the default value,
+ # Let jail(8) to use the default because
+ # mount(8) only accepts an integer.
+ # This should accept a ruleset name.
+ ;;
+ *) warn "devfs_ruleset must be an integer." ;;
+ esac
+ fi
+ eval : \${jail_${_jv}_fdescfs_enable:=${jail_fdescfs_enable:-NO}}
+ if checkyesno jail_${_jv}_fdescfs_enable; then
+ echo " mount.fdescfs;"
+ fi
+ eval : \${jail_${_jv}_procfs_enable:=${jail_procfs_enable:-NO}}
+ if checkyesno jail_${_jv}_procfs_enable; then
+ echo " mount.procfs;"
+ fi
+
+ eval : \${jail_${_jv}_mount_enable:=${jail_mount_enable:-NO}}
+ if checkyesno jail_${_jv}_mount_enable; then
+ echo " allow.mount;"
+ fi
+
+ extract_var $_jv set_hostname_allow allow.set_hostname YN NO
+ extract_var $_jv sysvipc_allow allow.sysvipc YN NO
+ extract_var $_jv enforce_statfs enforce_statfs - 2
+ extract_var $_jv osreldate osreldate
+ extract_var $_jv osrelease osrelease
+
+ _zfs_dataset="$(eval echo \$jail_${_jv}_zfs_dataset)"
+ if [ -n "$_zfs_dataset" ]; then
+ for ds in $_zfs_dataset; do
+ echo " zfs.dataset += ${ds};"
+ done
+ fi
+ for _p in $_parameters; do
+ echo " ${_p%\;};"
+ done
+ echo "}"
+ ) >> $_conf
+
+ return 0
+}
+
+# jail_extract_address argument iface
+# The second argument is the string from one of the _ip
+# or the _multi variables. In case of a comma separated list
+# only one argument must be passed in at a time.
+# The function alters the _type, _iface, _addr and _mask variables.
+#
+jail_extract_address()
+{
+ local _i _interface
+ _i=$1
+ _interface=$2
+
+ if [ -z "${_i}" ]; then
+ warn "jail_extract_address: called without input"
+ return
+ fi
+
+ # Check if we have an interface prefix given and split into
+ # iFace and rest.
+ case "${_i}" in
+ *\|*) # ifN|.. prefix there
+ _iface=${_i%%|*}
+ _r=${_i##*|}
+ ;;
+ *) _iface=""
+ _r=${_i}
+ ;;
+ esac
+
+ # In case the IP has no interface given, check if we have a global one.
+ _iface=${_iface:-${_interface}}
+
+ # Set address, cut off any prefix/netmask/prefixlen.
+ _addr=${_r}
+ _addr=${_addr%%[/ ]*}
+
+ # Theoretically we can return here if interface is not set,
+ # as we only care about the _mask if we call ifconfig.
+ # This is not done because we may want to santize IP addresses
+ # based on _type later, and optionally change the type as well.
+
+ # Extract the prefix/netmask/prefixlen part by cutting off the address.
+ _mask=${_r}
+ _mask=`expr -- "${_mask}" : "${_addr}\(.*\)"`
+
+ # Identify type {inet,inet6}.
+ case "${_addr}" in
+ *\.*\.*\.*) _type="inet" ;;
+ *:*) _type="inet6" ;;
+ *) warn "jail_extract_address: type not identified"
+ ;;
+ esac
+
+ # Handle the special /netmask instead of /prefix or
+ # "netmask xxx" case for legacy IP.
+ # We do NOT support shortend class-full netmasks.
+ if [ "${_type}" = "inet" ]; then
+ case "${_mask}" in
+ /*\.*\.*\.*) _mask=" netmask ${_mask#/}" ;;
+ *) ;;
+ esac
+
+ # In case _mask is still not set use /32.
+ _mask=${_mask:-/32}
+
+ elif [ "${_type}" = "inet6" ]; then
+ # In case _mask is not set for IPv6, use /128.
+ _mask=${_mask:-/128}
+ fi
+}
+
+# jail_handle_ips_option input iface
+# Handle a single argument imput which can be a comma separated
+# list of addresses (theoretically with an option interface and
+# prefix/netmask/prefixlen).
+#
+jail_handle_ips_option()
+{
+ local _x _type _i _defif
+ _x=$1
+ _defif=$2
+
+ if [ -z "${_x}" ]; then
+ # No IP given. This can happen for the primary address
+ # of each address family.
+ return
+ fi
+
+ # Loop, in case we find a comma separated list, we need to handle
+ # each argument on its own.
+ while [ ${#_x} -gt 0 ]; do
+ case "${_x}" in
+ *,*) # Extract the first argument and strip it off the list.
+ _i=`expr -- "${_x}" : '^\([^,]*\)'`
+ _x=`expr -- "${_x}" : "^[^,]*,\(.*\)"`
+ ;;
+ *) _i=${_x}
+ _x=""
+ ;;
+ esac
+
+ _type=""
+ _addr=""
+ _mask=""
+ _iface=""
+ jail_extract_address $_i $_defif
+
+ # make sure we got an address.
+ case $_addr in
+ "") continue ;;
+ *) ;;
+ esac
+
+ # Append address to list of addresses for the jail command.
+ case $_type in
+ inet)
+ echo " ip4.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";"
+ ;;
+ inet6)
+ echo " ip6.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";"
+ need_dad_wait=1
+ ;;
+ esac
+ done
+}
+
+jail_config()
+{
+ local _j _jv
+
+ case $1 in
+ _ALL) return ;;
+ esac
+ for _j in $@; do
+ _j=$(echo $_j | tr /. _)
+ _jv=$(echo -n $_j | tr -c '[:alnum:]' _)
+ if parse_options $_j $_jv; then
+ echo "$_j: parameters are in $_conf."
+ fi
+ done
+}
+
+jail_console()
+{
+ local _j _jv _cmd
+
+ # One argument that is not _ALL.
+ case $#:$1 in
+ 0:*|1:_ALL) err 3 "Specify a jail name." ;;
+ 1:*) ;;
+ esac
+ _j=$(echo $1 | tr /. _)
+ _jv=$(echo -n $1 | tr -c '[:alnum:]' _)
+ shift
+ case $# in
+ 0) eval _cmd=\${jail_${_jv}_consolecmd:-$jail_consolecmd} ;;
+ *) _cmd=$@ ;;
+ esac
+ $jail_jexec $_j $_cmd
+}
+
+jail_status()
+{
+
+ $jail_jls -N
+}
+
+jail_start()
+{
+ local _j _jv _jid _id _name
+
+ if [ $# = 0 ]; then
+ return
+ fi
+ startmsg -n 'Starting jails:'
+ case $1 in
+ _ALL)
+ command=$jail_program
+ rc_flags=$jail_flags
+ command_args="-f $jail_conf -c"
+ if ! checkyesno jail_parallel_start; then
+ command_args="$command_args -p1"
+ fi
+ _tmp=`mktemp -t jail` || exit 3
+ if $command $rc_flags $command_args >> $_tmp 2>&1; then
+ $jail_jls jid name | while read _id _name; do
+ startmsg -n " $_name"
+ echo $_id > /var/run/jail_${_name}.id
+ done
+ else
+ cat $_tmp
+ fi
+ rm -f $_tmp
+ startmsg '.'
+ return
+ ;;
+ esac
+ if checkyesno jail_parallel_start; then
+ #
+ # Start jails in parallel and then check jail id when
+ # jail_parallel_start is YES.
+ #
+ for _j in $@; do
+ _j=$(echo $_j | tr /. _)
+ _jv=$(echo -n $_j | tr -c '[:alnum:]' _)
+ parse_options $_j $_jv || continue
+
+ eval rc_flags=\${jail_${_jv}_flags:-$jail_flags}
+ eval command=\${jail_${_jv}_program:-$jail_program}
+ command_args="-i -f $_conf -c $_j"
+ (
+ _tmp=`mktemp -t jail_${_j}` || exit 3
+ if $command $rc_flags $command_args \
+ >> $_tmp 2>&1 </dev/null; then
+ startmsg -n " ${_hostname:-${_j}}"
+ _jid=$($jail_jls -j $_j jid)
+ echo $_jid > /var/run/jail_${_j}.id
+ else
+ startmsg " cannot start jail " \
+ "\"${_hostname:-${_j}}\": "
+ cat $_tmp
+ fi
+ rm -f $_tmp
+ ) &
+ done
+ wait
+ else
+ #
+ # Start jails one-by-one when jail_parallel_start is NO.
+ #
+ for _j in $@; do
+ _j=$(echo $_j | tr /. _)
+ _jv=$(echo -n $_j | tr -c '[:alnum:]' _)
+ parse_options $_j $_jv || continue
+
+ eval rc_flags=\${jail_${_jv}_flags:-$jail_flags}
+ eval command=\${jail_${_jv}_program:-$jail_program}
+ command_args="-i -f $_conf -c $_j"
+ _tmp=`mktemp -t jail` || exit 3
+ if $command $rc_flags $command_args \
+ >> $_tmp 2>&1 </dev/null; then
+ startmsg -n " ${_hostname:-${_j}}"
+ _jid=$($jail_jls -j $_j jid)
+ echo $_jid > /var/run/jail_${_j}.id
+ else
+ startmsg " cannot start jail " \
+ "\"${_hostname:-${_j}}\": "
+ cat $_tmp
+ fi
+ rm -f $_tmp
+ done
+ fi
+ startmsg '.'
+}
+
+jail_stop()
+{
+ local _j _jv
+
+ if [ $# = 0 ]; then
+ return
+ fi
+ echo -n 'Stopping jails:'
+ case $1 in
+ _ALL)
+ command=$jail_program
+ rc_flags=$jail_flags
+ command_args="-f $jail_conf -r"
+ if checkyesno jail_reverse_stop; then
+ $jail_jls name | tail -r
+ else
+ $jail_jls name
+ fi | while read _j; do
+ echo -n " $_j"
+ _tmp=`mktemp -t jail` || exit 3
+ $command $rc_flags $command_args $_j >> $_tmp 2>&1
+ if $jail_jls -j $_j > /dev/null 2>&1; then
+ cat $_tmp
+ else
+ rm -f /var/run/jail_${_j}.id
+ fi
+ rm -f $_tmp
+ done
+ echo '.'
+ return
+ ;;
+ esac
+ checkyesno jail_reverse_stop && set -- $(reverse_list $@)
+ for _j in $@; do
+ _j=$(echo $_j | tr /. _)
+ _jv=$(echo -n $_j | tr -c '[:alnum:]' _)
+ parse_options $_j $_jv || continue
+ if ! $jail_jls -j $_j > /dev/null 2>&1; then
+ continue
+ fi
+ eval command=\${jail_${_jv}_program:-$jail_program}
+ echo -n " ${_hostname:-${_j}}"
+ _tmp=`mktemp -t jail` || exit 3
+ $command -q -f $_conf -r $_j >> $_tmp 2>&1
+ if $jail_jls -j $_j > /dev/null 2>&1; then
+ cat $_tmp
+ else
+ rm -f /var/run/jail_${_j}.id
+ fi
+ rm -f $_tmp
+ done
+ echo '.'
+}
+
+jail_warn()
+{
+
+ # To relieve confusion, show a warning message.
+ case $_confwarn in
+ 1) warn "Per-jail configuration via jail_* variables " \
+ "is obsolete. Please consider migrating to $jail_conf."
+ ;;
+ esac
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+jail_svcj="NO"
+
+case $# in
+1) run_rc_command $@ ${jail_list:-_ALL} ;;
+*) jail_reverse_stop="no"
+ run_rc_command $@ ;;
+esac
diff --git a/libexec/rc/rc.d/kadmind b/libexec/rc/rc.d/kadmind
new file mode 100755
index 000000000000..0cee49630480
--- /dev/null
+++ b/libexec/rc/rc.d/kadmind
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: kadmind
+# REQUIRE: kdc
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name=kadmind
+desc="Server for administrative access to Kerberos database"
+rcvar=${name}_enable
+required_vars=kdc_enable
+command_args="$command_args &"
+
+: ${kadmind_svcj_options:="net_basic"}
+
+set_rcvar_obsolete kadmind5_server_enable kadmind_enable
+set_rcvar_obsolete kadmind5_server kadmind_program
+set_rcvar_obsolete kerberos5_server_enable kdc_enable
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/kdc b/libexec/rc/rc.d/kdc
new file mode 100755
index 000000000000..204b08f1e99c
--- /dev/null
+++ b/libexec/rc/rc.d/kdc
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: kdc
+# REQUIRE: NETWORKING
+# BEFORE: SERVERS
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name=kdc
+desc="Kerberos 5 server"
+rcvar=${name}_enable
+: ${kdc_restart:="NO"}
+: ${kdc_restart_delay:=""}
+: ${kdc_svcj_options:="net_basic"}
+
+set_rcvar_obsolete kerberos5_server_enable kdc_enable
+set_rcvar_obsolete kerberos5_server kdc_program
+set_rcvar_obsolete kerberos5_server_flags kdc_flags
+
+default_kdc_programs='/usr/libexec/kdc /usr/libexec/kdc /usr/libexec/krb5kdc /usr/local/sbin/krb5kdc'
+
+load_rc_config $name
+
+# XXX Remove the following block of code when Heimdal is removed
+if [ -z "${kdc_program}" ]; then
+ for i in ${default_kdc_programs}; do
+ if [ -x "${i}" ]; then
+ kdc_program=${i}
+ break
+ fi
+ done
+fi
+
+command="${kdc_program}"
+
+if [ "${kdc_program}" = /usr/libexec/kdc -o \
+ "${kdc_program}" = /usr/local/libexec/kdc ]; then
+ detach="--detach"
+ flavor=heimdal
+else
+ flavor=mit
+ unset detach
+fi
+
+case ${kdc_restart} in
+[Yy][Ee][Ss])
+ if [ "$flavor" = mit ]; then
+ detach=-n
+ else
+ unset detach
+ fi
+ case ${kdc_restart_delay} in
+ "") unset daemon_restart_delay;;
+ *) daemon_restart_delay="-R ${kdc_restart_delay}";;
+ esac
+ command_args="-r ${daemon_restart_delay} ${kdc_program} ${detach} ${command_args}"
+ kdc_program=/usr/sbin/daemon
+ ;;
+*)
+ command_args="${detach} ${command_args}"
+ ;;
+esac
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/kfd b/libexec/rc/rc.d/kfd
new file mode 100755
index 000000000000..23ad790abab5
--- /dev/null
+++ b/libexec/rc/rc.d/kfd
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: kfd
+# REQUIRE: NETWORKING
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name=kfd
+desc="Receive forwarded tickets"
+rcvar=${name}_enable
+command_args="$command_args -i &"
+
+: ${kfd_svcj_options:="net_basic"}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/kld b/libexec/rc/rc.d/kld
new file mode 100755
index 000000000000..37b14255abb9
--- /dev/null
+++ b/libexec/rc/rc.d/kld
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+# Copyright (c) 2011 Douglas Barton
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+# PROVIDE: kld
+# REQUIRE: kldxref
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="kld"
+desc="Load kernel modules"
+
+start_cmd="${name}_start"
+stop_cmd=':'
+
+kld_start()
+{
+ [ -n "$kld_list" ] || return
+ [ -z "$(kenv -q kld_disable 2>/dev/null)" ] || return
+
+ local _kld
+
+ echo 'Loading kernel modules:' $kld_list
+ for _kld in $kld_list ; do
+ load_kld -e ${_kld}.ko $_kld
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+kld_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/kldxref b/libexec/rc/rc.d/kldxref
new file mode 100755
index 000000000000..d6aa02d778d9
--- /dev/null
+++ b/libexec/rc/rc.d/kldxref
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: kldxref
+# REQUIRE: mountcritlocal
+# BEFORE: netif
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+rcvar="kldxref_enable"
+name="kldxref"
+desc="Generate hints for the kernel loader"
+stop_cmd=":"
+start_cmd="kldxref_start"
+
+kldxref_start() {
+ if [ -n "$kldxref_module_path" ]; then
+ MODULE_PATHS="$kldxref_module_path"
+ else
+ MODULE_PATHS=`sysctl -n kern.module_path`
+ fi
+ IFS=';'
+ for MODULE_DIR in $MODULE_PATHS; do
+ if checkyesno kldxref_clobber ||
+ [ ! -f "$MODULE_DIR/linker.hints" ] &&
+ [ `echo ${MODULE_DIR}/*.ko` != "${MODULE_DIR}/*.ko" ]; then
+ echo "Building $MODULE_DIR/linker.hints"
+ kldxref "$MODULE_DIR"
+ fi
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+kldxref_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/kpasswdd b/libexec/rc/rc.d/kpasswdd
new file mode 100755
index 000000000000..7e2562769640
--- /dev/null
+++ b/libexec/rc/rc.d/kpasswdd
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: kpasswdd
+# REQUIRE: kdc
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name=kpasswdd
+desc="Kerberos 5 password changing"
+rcvar=${name}_enable
+required_vars=kdc_enable
+command_args="$command_args &"
+
+: ${kpasswdd_svcj_options:="net_basic"}
+
+set_rcvar_obsolete kpasswdd_server_enable kpasswdd_enable
+set_rcvar_obsolete kpasswdd_server kpasswdd_program
+set_rcvar_obsolete kerberos5_server_enable kdc_enable
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ldconfig b/libexec/rc/rc.d/ldconfig
new file mode 100755
index 000000000000..494228e96501
--- /dev/null
+++ b/libexec/rc/rc.d/ldconfig
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ldconfig
+# REQUIRE: FILESYSTEMS
+# BEFORE: DAEMON
+
+. /etc/rc.subr
+
+name="ldconfig"
+desc="Configure the shared library cache"
+ldconfig_command="/sbin/ldconfig"
+start_cmd="ldconfig_start"
+stop_cmd=":"
+
+ldconfig_paths()
+{
+ local _dirs _files _ii _ldpaths _paths
+
+ _dirs="${1}"
+ _paths="${2}"
+ _ldpaths="${3}"
+
+ for _ii in ${_dirs}; do
+ if [ -d "${_ii}" ]; then
+ _files=`find ${_ii} -type f`
+ if [ -n "${_files}" ]; then
+ _paths="${_paths} `cat ${_files} | sort -u`"
+ fi
+ fi
+ done
+ for _ii in ${_paths}; do
+ if [ -r "${_ii}" ]; then
+ _ldpaths="${_ldpaths} ${_ii}"
+ fi
+ done
+
+ echo "${_ldpaths}"
+}
+
+ldconfig_start()
+{
+ local _files _ins
+
+ _ins=
+ ldconfig=${ldconfig_command}
+ checkyesno ldconfig_insecure && _ins="-i"
+ if [ -x "${ldconfig_command}" ]; then
+ _LDC=$(/libexec/ld-elf.so.1 -v | sed -n -e '/^Default lib path /s///p' | tr : ' ')
+ _LDC=$(ldconfig_paths "${ldconfig_local_dirs}" \
+ "${ldconfig_paths} /etc/ld-elf.so.conf" "$_LDC")
+ startmsg 'ELF ldconfig path:' ${_LDC}
+ ${ldconfig} -elf ${_ins} ${_LDC}
+
+ if check_kern_features compat_freebsd32; then
+ _LDC=""
+ if [ -x /libexec/ld-elf32.so.1 ]; then
+ for x in $(/libexec/ld-elf32.so.1 -v | sed -n -e '/^Default lib path /s///p' | tr : ' '); do
+ if [ -d "${x}" ]; then
+ _LDC="${_LDC} ${x}"
+ fi
+ done
+ fi
+ _LDC=$(ldconfig_paths "${ldconfig_local32_dirs}" \
+ "${ldconfig32_paths}" "$_LDC")
+ startmsg '32-bit compatibility ldconfig path:' ${_LDC}
+ ${ldconfig} -32 ${_ins} ${_LDC}
+ fi
+
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+ldconfig_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/linux b/libexec/rc/rc.d/linux
new file mode 100755
index 000000000000..d419920acaca
--- /dev/null
+++ b/libexec/rc/rc.d/linux
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: linux
+# REQUIRE: kldxref zfs
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="linux"
+desc="Enable Linux ABI"
+rcvar="linux_enable"
+start_cmd="${name}_start"
+stop_cmd=":"
+
+linux_mount() {
+ local _fs _mount_point
+ _fs="$1"
+ _mount_point="$2"
+ shift 2
+ if ! mount | grep -q "^$_fs on $_mount_point ("; then
+ mkdir -p "$_mount_point"
+ mount "$@" -t "$_fs" "$_fs" "$_mount_point"
+ fi
+}
+
+linux_start()
+{
+ local _emul_path _tmpdir
+
+ case `sysctl -n hw.machine_arch` in
+ aarch64)
+ load_kld -e 'linux64elf' linux64
+ ;;
+ amd64)
+ load_kld -e 'linuxelf' linux
+ load_kld -e 'linux64elf' linux64
+ ;;
+ i386)
+ load_kld -e 'linuxelf' linux
+ ;;
+ esac
+
+ _emul_path="$(sysctl -n compat.linux.emul_path)"
+
+ if [ -x ${_emul_path}/sbin/ldconfigDisabled ]; then
+ _tmpdir=`mktemp -d -t linux-ldconfig`
+ ${_emul_path}/sbin/ldconfig -C ${_tmpdir}/ld.so.cache
+ if ! cmp -s ${_tmpdir}/ld.so.cache ${_emul_path}/etc/ld.so.cache; then
+ cat ${_tmpdir}/ld.so.cache > ${_emul_path}/etc/ld.so.cache
+ fi
+ rm -rf ${_tmpdir}
+ fi
+
+ # Linux uses the pre-pts(4) tty naming scheme.
+ load_kld pty
+
+ # Explicitly load the filesystem modules; they are usually required,
+ # even with linux_mounts_enable="NO".
+ load_kld fdescfs
+ load_kld linprocfs
+ load_kld linsysfs
+
+ # Handle unbranded ELF executables by defaulting to ELFOSABI_LINUX.
+ if [ `sysctl -ni kern.elf64.fallback_brand` -eq "-1" ]; then
+ sysctl kern.elf64.fallback_brand=3 > /dev/null
+ fi
+
+ if [ `sysctl -ni kern.elf32.fallback_brand` -eq "-1" ]; then
+ sysctl kern.elf32.fallback_brand=3 > /dev/null
+ fi
+
+ if checkyesno linux_mounts_enable; then
+ linux_mount linprocfs "${_emul_path}/proc" -o nocover
+ linux_mount linsysfs "${_emul_path}/sys" -o nocover
+ linux_mount devfs "${_emul_path}/dev" -o nocover
+ linux_mount fdescfs "${_emul_path}/dev/fd" -o nocover,linrdlnk
+ linux_mount tmpfs "${_emul_path}/dev/shm" -o nocover,mode=1777
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: kernel modules and FS-mounting
+linux_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/local b/libexec/rc/rc.d/local
new file mode 100755
index 000000000000..c3f5e037563e
--- /dev/null
+++ b/libexec/rc/rc.d/local
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: local
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="local"
+desc="Run /etc/rc.local and /etc/rc.shutdown.local"
+start_cmd="local_start"
+stop_cmd="local_stop"
+
+local_start()
+{
+ if [ -f /etc/rc.local ]; then
+ startmsg -n 'Starting local daemons:'
+ . /etc/rc.local
+ startmsg '.'
+ fi
+}
+
+local_stop()
+{
+ if [ -f /etc/rc.shutdown.local ]; then
+ echo -n 'Shutting down local daemons:'
+ . /etc/rc.shutdown.local
+ echo '.'
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: it may contain everything
+local_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/local_unbound b/libexec/rc/rc.d/local_unbound
new file mode 100755
index 000000000000..94f01810b303
--- /dev/null
+++ b/libexec/rc/rc.d/local_unbound
@@ -0,0 +1,123 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: local_unbound
+# REQUIRE: FILESYSTEMS defaultroute netwait resolv
+# BEFORE: NETWORKING
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="local_unbound"
+desc="Local caching forwarding resolver"
+rcvar="local_unbound_enable"
+
+command="/usr/sbin/local-unbound"
+extra_commands="anchor configtest reload setup"
+start_precmd="local_unbound_prestart"
+start_postcmd="local_unbound_poststart"
+reload_precmd="local_unbound_configtest"
+anchor_cmd="local_unbound_anchor"
+configtest_cmd="local_unbound_configtest"
+setup_cmd="local_unbound_setup"
+pidfile="/var/run/${name}.pid"
+
+load_rc_config $name
+
+: ${local_unbound_workdir:=/var/unbound}
+: ${local_unbound_config:=${local_unbound_workdir}/unbound.conf}
+: ${local_unbound_flags:="-c ${local_unbound_config}"}
+: ${local_unbound_forwardconf:=${local_unbound_workdir}/forward.conf}
+: ${local_unbound_controlconf:=${local_unbound_workdir}/control.conf}
+: ${local_unbound_anchor:=${local_unbound_workdir}/root.key}
+: ${local_unbound_forwarders:=}
+: ${local_unbound_tls:=}
+: ${local_unbound_pidfile:=${pidfile}}
+pidfile=${local_unbound_pidfile}
+: ${local_unbound_svcj_options:="net_basic"}
+
+do_as_unbound()
+{
+ echo "$@" | su -m unbound
+}
+
+#
+# Retrieve or update the DNSSEC root anchor
+#
+local_unbound_anchor()
+{
+ do_as_unbound ${command}-anchor -a ${local_unbound_anchor}
+ # we can't trust the exit code - check if the file exists
+ [ -f ${local_unbound_anchor} ]
+}
+
+#
+# Check the unbound configuration file
+#
+local_unbound_configtest()
+{
+ do_as_unbound ${command}-checkconf ${local_unbound_config}
+}
+
+#
+# Create the unbound configuration file and update resolv.conf to
+# point to unbound.
+#
+local_unbound_setup()
+{
+ local tls_flag
+ if checkyesno local_unbound_tls ; then
+ tls_flag="-t"
+ fi
+ echo "Performing initial setup."
+ ${command}-setup -n \
+ -u unbound \
+ -w ${local_unbound_workdir} \
+ -c ${local_unbound_config} \
+ -f ${local_unbound_forwardconf} \
+ -o ${local_unbound_controlconf} \
+ -a ${local_unbound_anchor} \
+ ${tls_flag} \
+ ${local_unbound_forwarders}
+}
+
+#
+# Before starting, check that the configuration file and root anchor
+# exist. If not, attempt to generate them.
+#
+local_unbound_prestart()
+{
+ # Create configuration file
+ if [ ! -f ${local_unbound_config} ] ; then
+ run_rc_command setup
+ fi
+
+ # Retrieve DNSSEC root key
+ if [ ! -s ${local_unbound_anchor} ] ; then
+ run_rc_command anchor
+ fi
+}
+
+#
+# After starting, wait for Unbound to report that it is ready to avoid
+# race conditions with services which require functioning DNS.
+#
+local_unbound_poststart()
+{
+ local retry=5
+
+ echo -n "Waiting for nameserver to start..."
+ until "${command}-control" -c "${local_unbound_config}" status | grep -q "is running" ; do
+ if [ $((retry -= 1)) -eq 0 ] ; then
+ echo " giving up"
+ return 1
+ fi
+ echo -n "."
+ sleep 1
+ done
+ echo " good"
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/localpkg b/libexec/rc/rc.d/localpkg
new file mode 100755
index 000000000000..12fb9e0fd927
--- /dev/null
+++ b/libexec/rc/rc.d/localpkg
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: localpkg
+# REQUIRE: sysvipc linux
+# BEFORE: securelevel
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="localpkg"
+desc="Run local init scripts"
+start_cmd="pkg_start"
+stop_cmd="pkg_stop"
+
+pkg_start()
+{
+ local initdone
+
+ # For each dir in $local_startup, search for init scripts matching *.sh
+ #
+ case ${local_startup} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ initdone=
+ find_local_scripts_old
+ for script in ${zlist} ${slist}; do
+ if [ -z "${initdone}" -a -f "${script}" ]; then
+ echo -n 'Local package initialization:'
+ initdone=yes
+ fi
+ if [ -x "${script}" ]; then
+ (set -T
+ trap 'exit 1' 2
+ ${script} start)
+ elif [ -f "${script}" -o -L "${script}" ]; then
+ echo -n " (skipping ${script}, not executable)"
+ fi
+ done
+ [ -n "${initdone}" ] && echo '.'
+ ;;
+ esac
+}
+
+pkg_stop()
+{
+ local initdone
+
+ case ${local_startup} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ initdone=
+ find_local_scripts_old
+ for script in `reverse_list ${slist} ${zlist}`; do
+ if [ -z "${initdone}" -a -f "${script}" ]; then
+ echo -n 'Shutting down local packages:'
+ initdone=yes
+ fi
+ if [ -x "${script}" ]; then
+ if [ `sysctl -n debug.bootverbose` -eq 1 ]; then
+ echo "==>" ${script}
+ fi
+ (set -T
+ trap 'exit 1' 2
+ ${script} stop)
+ elif [ -f "${script}" -o -L "${script}" ]; then
+ echo -n " (skipping ${script##*/}, not executable)"
+ fi
+ done
+ [ -n "${initdone}" ] && echo '.'
+ ;;
+ esac
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: other rc.d scripts need to decide on their own
+localpkg_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/lockd b/libexec/rc/rc.d/lockd
new file mode 100755
index 000000000000..9c804751031a
--- /dev/null
+++ b/libexec/rc/rc.d/lockd
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# FreeBSD History: src/etc/rc.d/nfslocking,v 1.11 2004/10/07 13:55:26 mtm
+#
+
+# PROVIDE: lockd
+# REQUIRE: nfsclient rpcbind statd
+# BEFORE: DAEMON
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="lockd"
+desc="NFS file locking daemon"
+rcvar=rpc_lockd_enable
+command="/usr/sbin/rpc.${name}"
+start_precmd='lockd_precmd'
+
+: ${lockd_svcj_options:="net_basic"}
+
+# Make sure that we are either an NFS client or server, and that we get
+# the correct flags from rc.conf(5).
+#
+lockd_precmd()
+{
+ force_depend rpcbind || return 1
+ force_depend statd rpc_statd || return 1
+}
+
+load_rc_config $name
+
+rc_flags=${rpc_lockd_flags}
+
+run_rc_command $1
diff --git a/libexec/rc/rc.d/lpd b/libexec/rc/rc.d/lpd
new file mode 100755
index 000000000000..0c169bef99a5
--- /dev/null
+++ b/libexec/rc/rc.d/lpd
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: lpd
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="lpd"
+desc="Line printer spooler daemon"
+rcvar="lpd_enable"
+command="/usr/sbin/${name}"
+required_files="/etc/printcap"
+start_precmd="chkprintcap"
+
+: ${lpd_svcj_options:="net_basic"}
+
+chkprintcap()
+{
+ if checkyesno chkprintcap_enable ; then
+ /usr/sbin/chkprintcap ${chkprintcap_flags}
+ fi
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/mdconfig b/libexec/rc/rc.d/mdconfig
new file mode 100755
index 000000000000..4df14017334b
--- /dev/null
+++ b/libexec/rc/rc.d/mdconfig
@@ -0,0 +1,199 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: mdconfig
+# REQUIRE: swap root
+
+. /etc/rc.subr
+
+name="mdconfig"
+desc="Create and control memory disks"
+stop_cmd="mdconfig_stop"
+start_cmd="mdconfig_start"
+start_precmd='[ -n "${_mdconfig_list}" ]'
+required_modules="geom_md:g_md"
+
+is_readonly()
+{
+ local _mp _ret
+
+ _mp=$1
+ _ret=`mount | while read _line; do
+ case ${_line} in
+ *" ${_mp} "*read-only*)
+ echo "yes"
+ ;;
+
+ *)
+ ;;
+ esac;
+ done`
+
+ if [ -n "${_ret}" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+init_variables()
+{
+ local _i
+
+ _fs=""
+ _mp=""
+ _dev="/dev/${_md}"
+ eval _config=\$mdconfig_${_md}
+ eval _newfs=\$mdconfig_${_md}_newfs
+
+ _type=${_config##*-t\ }
+ _type=${_type%%\ *}
+ if [ -z "${_type}" ]; then
+ err 1 "You need to specify \"-t <type>\" in mdconfig_${_md}"
+ fi
+
+ if [ "${_type}" = "vnode" ]; then
+ _file=${_config##*-f\ }
+ _file=${_file%%\ *}
+ if [ -z "${_file}" ]; then
+ err 2 "You need to specify \"-f <file>\" in mdconfig_${_md} for vnode devices"
+ fi
+ if [ "${_file}" != "${_file%.uzip}" ]; then
+ _dev="/dev/${_md}.uzip"
+ fi
+ for _i in `df ${_file} 2>/dev/null`; do _fs=${_i}; done
+ fi
+
+ # Debugging help.
+ debug "${_md} config: ${_config}"
+ debug "${_md} type: ${_type}"
+ debug "${_md} dev: ${_dev}"
+ debug "${_md} file: ${_file}"
+ debug "${_md} fs: ${_fs}"
+ debug "${_md} newfs flags: ${_newfs}"
+}
+
+mdconfig_start()
+{
+ local _md _mp _config _type _dev _file _fs _newfs _fsck_cmd
+
+ for _md in ${_mdconfig_list}; do
+ init_variables ${_md}
+ # Create md(4) devices of types swap, malloc and vnode if the
+ # file is on the root partition.
+ if [ "${_type}" != "vnode" -o "${_fs}" = "/" ]; then
+ if [ "${_type}" = "vnode" ]; then
+ if is_readonly ${_fs}; then
+ warn "${_fs} is mounted read-only, skipping ${_md}."
+ continue
+ fi
+ if [ "${_file}" != "${_file%.uzip}" ]; then
+ load_kld -m g_uzip geom_uzip || return 3
+ # sleep a bit to allow creation of /dev/mdX.uzip
+ sleep 2
+ fi
+ fi
+ if mdconfig -l -u ${_md} >/dev/null 2>&1; then
+ err 3 "${_md} already exists"
+ fi
+ echo "Creating ${_md} device (${_type})."
+ if ! mdconfig -a ${_config} -u ${_md}; then
+ echo "Creating ${_md} device failed, moving on."
+ continue
+ fi
+ # Skip fsck for uzip devices.
+ if [ "${_type}" = "vnode" ]; then
+ if [ "${_file}" != "${_file%.uzip}" ]; then
+ _fsck_cmd=":"
+ elif checkyesno background_fsck; then
+ _fsck_cmd="fsck -F"
+ else
+ _fsck_cmd="fsck"
+ fi
+ if ! eval ${_fsck_cmd} -p ${_dev} >/dev/null; then
+ echo "Fsck failed on ${_dev}, not mounting the filesystem."
+ continue
+
+ fi
+ else
+ newfs ${_newfs} ${_dev} >/dev/null
+ fi
+ if mount -d ${_dev} 2>&1 >/dev/null; then
+ echo "Mounting ${_dev}."
+ mount ${_dev}
+ fi
+ fi
+ done
+}
+
+mdconfig_stop()
+{
+ local _md _mp _config _type _dev _file _fs _newfs _i
+
+ for _md in ${_mdconfig_list}; do
+ init_variables ${_md}
+ if [ "${_type}" != "vnode" -o "${_fs}" = "/" ]; then
+ for _i in `df ${_dev} 2>/dev/null`; do _mp=${_i}; done
+ if [ -z "${_mp}" -o "${_mp}" != "${_mp%%%}" ]; then
+ echo "Device ${_dev} isn't mounted."
+ else
+ echo "Umounting ${_dev}."
+ umount ${_dev}
+ fi
+ if mdconfig -l -u ${_md} >/dev/null 2>&1; then
+ echo "Destroying ${_md}."
+ mdconfig -d -u ${_md}
+ fi
+ fi
+ done
+}
+
+_mdconfig_cmd="$1"
+if [ $# -gt 0 ]; then
+ shift
+fi
+[ -n "$*" ] && _mdconfig_list="$*"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+mdconfig_svcj="NO"
+
+if [ -z "${_mdconfig_list}" ]; then
+ for _mdconfig_config in `list_vars mdconfig_md[0-9]\* |
+ sort_lite -nk1.12`
+ do
+ _mdconfig_unit=${_mdconfig_config#mdconfig_md}
+ [ "${_mdconfig_unit#*[!0-9]}" = "$_mdconfig_unit" ] ||
+ continue
+ _mdconfig_list="$_mdconfig_list md$_mdconfig_unit"
+ done
+ _mdconfig_list="${_mdconfig_list# }"
+fi
+
+run_rc_command "${_mdconfig_cmd}"
diff --git a/libexec/rc/rc.d/mdconfig2 b/libexec/rc/rc.d/mdconfig2
new file mode 100755
index 000000000000..716e71cd2a32
--- /dev/null
+++ b/libexec/rc/rc.d/mdconfig2
@@ -0,0 +1,229 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: mdconfig2
+# REQUIRE: mountcritremote
+# BEFORE: SERVERS
+
+. /etc/rc.subr
+
+name="mdconfig2"
+desc="Create and control memory disks"
+stop_cmd="mdconfig2_stop"
+start_cmd="mdconfig2_start"
+start_precmd='[ -n "${_mdconfig2_list}" ]'
+required_modules="geom_md:g_md"
+
+is_readonly()
+{
+ local _mp _ret
+
+ _mp=$1
+ _ret=`mount | while read _line; do
+ case ${_line} in
+ *" ${_mp} "*read-only*)
+ echo "yes"
+ ;;
+
+ *)
+ ;;
+ esac;
+ done`
+
+ if [ -n "${_ret}" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+init_variables()
+{
+ local _i
+
+ _fs=""
+ _mp=""
+ _mounted="no"
+ _dev="/dev/${_md}"
+ eval _config=\$mdconfig_${_md}
+ eval _owner=\$mdconfig_${_md}_owner
+ eval _perms=\$mdconfig_${_md}_perms
+ eval _files=\$mdconfig_${_md}_files
+ eval _populate=\$mdconfig_${_md}_cmd
+
+ _type=${_config##*-t\ }
+ _type=${_type%%\ *}
+ if [ -z "${_type}" ]; then
+ err 1 "You need to specify \"-t <type>\" in mdconfig_${_md}"
+ fi
+
+ if [ "${_type}" = "vnode" ]; then
+ _file=${_config##*-f\ }
+ _file=${_file%%\ *}
+ if [ -z "${_file}" ]; then
+ err 2 "You need to specify \"-f <file>\" in mdconfig_${_md} for vnode devices"
+ fi
+
+ if [ "${_file}" != "${_file%.uzip}" ]; then
+ _dev="/dev/${_md}.uzip"
+ fi
+ for _i in `df ${_file} 2>/dev/null`; do _fs=${_i}; done
+ fi
+
+ # Debugging help.
+ debug "${_md} config: ${_config}"
+ debug "${_md} type: ${_type}"
+ debug "${_md} dev: ${_dev}"
+ debug "${_md} file: ${_file}"
+ debug "${_md} fs: ${_fs}"
+ debug "${_md} owner: ${_owner}"
+ debug "${_md} perms: ${_perms}"
+ debug "${_md} files: ${_files}"
+ debug "${_md} populate cmd: ${_populate}"
+}
+
+mdconfig2_start()
+{
+ local _md _fs _mp _mounted _dev _config _type _file _owner _perms _files _populate _fsck_cmd _i
+
+ for _md in ${_mdconfig2_list}; do
+ init_variables ${_md}
+ if [ ! -r ${_file} ]; then
+ err 3 "${_file} doesn't exist"
+ continue
+ fi
+ # First pass: create md(4) vnode devices from files stored on
+ # non-root partition. Swap and malloc md(4) devices have already
+ # been created.
+ if [ "${_type}" = "vnode" -a "${_fs}" != "/" ]; then
+ if [ "${_file}" != "${_file%.uzip}" ]; then
+ load_kld -m g_uzip geom_uzip || return 3
+ fi
+ if is_readonly ${_fs}; then
+ warn "${_fs} is mounted read-only, skipping ${_md}."
+ continue
+ fi
+ if mdconfig -l -u ${_md} >/dev/null 2>&1; then
+ err 3 "${_md} already exists"
+ fi
+ echo "Creating ${_md} device (${_type})."
+ if ! mdconfig -a ${_config} -u ${_md}; then
+ echo "Creating ${_md} device failed, moving on."
+ continue
+ fi
+ # Skip fsck for uzip devices.
+ if [ "${_file}" != "${_file%.uzip}" ]; then
+ _fsck_cmd=":"
+ elif checkyesno background_fsck; then
+ _fsck_cmd="fsck -F"
+ else
+ _fsck_cmd="fsck"
+ fi
+ if ! eval ${_fsck_cmd} -p ${_dev} >/dev/null; then
+ echo "Fsck failed on ${_dev}, not mounting the filesystem."
+ continue
+ fi
+ if mount -d ${_dev} >/dev/null 2>&1; then
+ echo "Mounting ${_dev}."
+ mount ${_dev}
+ fi
+ fi
+
+ for _i in `df ${_dev} 2>/dev/null`; do _mp=${_i}; done
+ if [ ! -z "${_mp}" -a "${_mp}" = "${_mp%%%}" ]; then
+ _mounted="yes"
+ fi
+
+ if checkyesno _mounted; then
+ # Second pass: change permissions and ownership.
+ [ -z "${_owner}" ] || chown -f ${_owner} ${_dev} ${_mp}
+ [ -z "${_perms}" ] || chmod -f ${_perms} ${_dev} ${_mp}
+
+ # Third pass: populate with foreign files.
+ if [ -n "${_files}" -o -n "${_populate}" ]; then
+ echo "Populating ${_dev}."
+ fi
+ if [ -n "${_files}" ]; then
+ cp -Rp ${_files} ${_mp}
+ fi
+ if [ -n "${_populate}" ]; then
+ eval ${_populate}
+ fi
+ fi
+ done
+}
+
+mdconfig2_stop()
+{
+ local _md _fs _mp _mounted _dev _config _type _file _owner _perms _files _populate
+
+ for _md in ${_mdconfig2_list}; do
+ init_variables ${_md}
+ if [ "${_type}" = "vnode" ]; then
+ for i in `df ${_dev} 2>/dev/null`; do _mp=$i; done
+ if [ ! -r "${_file}" -o "${_fs}" = "/" ]; then
+ continue
+ fi
+ if [ -z "${_mp}" -o "${_mp}" != "${_mp%%%}" ]; then
+ echo "Device ${_dev} isn't mounted."
+ else
+ echo "Umounting ${_dev}."
+ umount ${_dev}
+ fi
+ if mdconfig -l -u ${_md} >/dev/null 2>&1; then
+ echo "Destroying ${_md}."
+ mdconfig -d -u ${_md}
+ fi
+ fi
+ done
+}
+
+_mdconfig2_cmd="$1"
+if [ $# -gt 0 ]; then
+ shift
+fi
+[ -n "$*" ] && _mdconfig2_list="$*"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+mdconfig2_svcj="NO"
+
+if [ -z "${_mdconfig2_list}" ]; then
+ for _mdconfig2_config in `list_vars mdconfig_md[0-9]\* |
+ sort_lite -nk1.12`
+ do
+ _mdconfig2_unit=${_mdconfig2_config#mdconfig_md}
+ [ "${_mdconfig2_unit#*[!0-9]}" = "$_mdconfig2_unit" ] ||
+ continue
+ _mdconfig2_list="$_mdconfig2_list md$_mdconfig2_unit"
+ done
+ _mdconfig2_list="${_mdconfig2_list# }"
+fi
+
+run_rc_command "${_mdconfig2_cmd}"
diff --git a/libexec/rc/rc.d/mixer b/libexec/rc/rc.d/mixer
new file mode 100755
index 000000000000..7527e16918d2
--- /dev/null
+++ b/libexec/rc/rc.d/mixer
@@ -0,0 +1,107 @@
+#!/bin/sh -
+#
+# Copyright (c) 2004 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: mixer
+# REQUIRE: FILESYSTEMS
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="mixer"
+desc="Save and restore soundcard mixer values"
+rcvar="mixer_enable"
+stop_cmd="mixer_stop"
+start_cmd="mixer_start"
+reload_cmd="mixer_start"
+extra_commands="reload"
+
+#
+# List current mixer devices to stdout.
+#
+list_mixers()
+{
+ ( cd /dev ; ls mixer* 2>/dev/null )
+}
+
+#
+# Save state of an individual mixer specified as $1
+#
+mixer_save()
+{
+ local dev
+
+ dev="/dev/${1}"
+ if [ -r ${dev} ]; then
+ /usr/sbin/mixer -f ${dev} -o > /var/db/${1}-state 2>/dev/null
+ fi
+}
+
+#
+# Restore the state of an individual mixer specified as $1
+#
+mixer_restore()
+{
+ local file dev
+
+ dev="/dev/${1}"
+ file="/var/db/${1}-state"
+ if [ -r ${dev} -a -r ${file} ]; then
+ /usr/sbin/mixer -f ${dev} `cat ${file}` > /dev/null
+ fi
+}
+
+#
+# Restore state of all mixers
+#
+mixer_start()
+{
+ local mixer
+
+ for mixer in `list_mixers`; do
+ mixer_restore ${mixer}
+ done
+}
+
+#
+# Save the state of all mixers
+#
+mixer_stop()
+{
+ local mixer
+
+ for mixer in `list_mixers`; do
+ mixer_save ${mixer}
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+mixer_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/motd b/libexec/rc/rc.d/motd
new file mode 100755
index 000000000000..7858aef2c3fe
--- /dev/null
+++ b/libexec/rc/rc.d/motd
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: motd
+# REQUIRE: mountcritremote FILESYSTEMS
+# BEFORE: LOGIN
+
+. /etc/rc.subr
+
+name="motd"
+desc="Update /var/run/motd"
+rcvar="update_motd"
+start_cmd="motd_start"
+stop_cmd=":"
+
+COMPAT_MOTD="/etc/motd"
+TARGET="/var/run/motd"
+TEMPLATE="/etc/motd.template"
+PERMS="644"
+
+motd_start()
+{
+ # Update kernel info in /var/run/motd
+ # Must be done *before* interactive logins are possible
+ # to prevent possible race conditions.
+ #
+ startmsg -n 'Updating motd:'
+ if [ ! -f "${TEMPLATE}" ]; then
+ # Create missing template from existing regular motd file, if
+ # one exists.
+ if [ -f "${COMPAT_MOTD}" ]; then
+ sed '1{/^FreeBSD.*/{d;};};' "${COMPAT_MOTD}" > "${TEMPLATE}"
+ chmod $PERMS "${TEMPLATE}"
+ rm -f "${COMPAT_MOTD}"
+ else
+ # Otherwise, create an empty template file.
+ install -c -o root -g wheel -m ${PERMS} /dev/null "${TEMPLATE}"
+ fi
+ fi
+ # Provide compatibility symlink:
+ if [ ! -h "${COMPAT_MOTD}" ]; then
+ ln -sF "${TARGET}" "${COMPAT_MOTD}"
+ fi
+
+ T=`mktemp -t motd`
+ uname -v | sed -e 's,^\([^#]*\) #\(.* [1-2][0-9][0-9][0-9]\).*/\([^\]*\)$,\1 (\3) #\2,' \
+ -e 's,^\([^ ]*\) \([^ ]*\) \([^ ]*\) \([^ ]*\)$,\1 \2 (\4) \3,' > ${T}
+ cat "${TEMPLATE}" >> ${T}
+
+ install -C -o root -g wheel -m "${PERMS}" "$T" "${TARGET}"
+ rm -f "$T"
+
+ startmsg '.'
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+motd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/mountcritlocal b/libexec/rc/rc.d/mountcritlocal
new file mode 100755
index 000000000000..5b80d4bfbb50
--- /dev/null
+++ b/libexec/rc/rc.d/mountcritlocal
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: mountcritlocal
+# REQUIRE: root hostid_save mdconfig
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="mountcritlocal"
+desc="Mount critical local filesystems"
+start_cmd="mountcritlocal_start"
+stop_cmd=sync
+
+mountcritlocal_start()
+{
+ local err holders waited
+
+ # Set up the list of network filesystem types for which mounting
+ # should be delayed until after network initialization.
+ case ${extra_netfs_types} in
+ [Nn][Oo])
+ ;;
+ *)
+ netfs_types="${netfs_types} ${extra_netfs_types}"
+ ;;
+ esac
+
+ while read a b vfstype rest; do
+ if [ "$vfstype" = "zfs" -a "${a#\#}" = "$a" ]; then
+ # zpool is needed for legacy ZFS
+ echo 'Importing zpools for legacy ZFS'
+ /etc/rc.d/zpool start
+ break
+ fi
+ done < /etc/fstab
+
+ # Mount everything except nfs filesystems.
+ startmsg -n 'Mounting local filesystems:'
+ mount_excludes='no'
+ for i in ${netfs_types}; do
+ fstype=${i%:*}
+ mount_excludes="${mount_excludes}${fstype},"
+ done
+ mount_excludes=${mount_excludes%,}
+
+ mount -a -t ${mount_excludes}
+ err=$?
+ if [ ${err} -ne 0 ]; then
+ echo 'Mounting /etc/fstab filesystems failed,' \
+ 'will retry after root mount hold release'
+ root_hold_wait
+ mount -a -t ${mount_excludes}
+ err=$?
+ fi
+
+ startmsg '.'
+
+ case ${err} in
+ 0)
+ ;;
+ *)
+ echo 'Mounting /etc/fstab filesystems failed,' \
+ 'startup aborted'
+ stop_boot true
+ ;;
+ esac
+}
+
+load_rc_config $name
+
+# mounting shall not be performed in a svcj
+mountcritlocal_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/mountcritremote b/libexec/rc/rc.d/mountcritremote
new file mode 100755
index 000000000000..99becaefb10f
--- /dev/null
+++ b/libexec/rc/rc.d/mountcritremote
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: mountcritremote
+# REQUIRE: NETWORKING FILESYSTEMS ipsec netwait nfscbd
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="mountcritremote"
+desc="Mount critical remote filesystems"
+stop_cmd=":"
+start_cmd="mountcritremote_start"
+start_precmd="mountcritremote_precmd"
+
+# Mount NFS filesystems if present in /etc/fstab
+#
+# XXX When the vfsload() issues with nfsclient support and related sysctls
+# have been resolved, this block can be removed, and the condition that
+# skips nfs in the following block (for "other network filesystems") can
+# be removed.
+#
+mountcritremote_precmd()
+{
+ case "`mount -d -a -t nfs 2> /dev/null`" in
+ *mount_nfs*)
+ # Handle absent nfs client support
+ load_kld -m nfs nfscl || return 1
+ ;;
+ esac
+ return 0
+}
+
+mountcritremote_start()
+{
+ local mounted_remote_filesystem=false
+
+ # Mount nfs filesystems.
+ #
+ case "`/sbin/mount -d -a -t nfs`" in
+ '')
+ ;;
+ *)
+ mounted_remote_filesystem=true
+ echo -n 'Mounting NFS filesystems:'
+ mount -a -t nfs
+ echo '.'
+ ;;
+ esac
+
+ # Mount other network filesystems if present in /etc/fstab.
+ case ${extra_netfs_types} in
+ [Nn][Oo])
+ ;;
+ *)
+ netfs_types="${netfs_types} ${extra_netfs_types}"
+ ;;
+ esac
+
+ for i in ${netfs_types}; do
+ fstype=${i%:*}
+ fsdecr=${i#*:}
+
+ [ "${fstype}" = "nfs" ] && continue
+
+ case "`mount -d -a -t ${fstype}`" in
+ *mount_${fstype}*)
+ mounted_remote_filesystem=true
+ echo -n "Mounting ${fsdecr} filesystems:"
+ mount -a -t ${fstype}
+ echo '.'
+ ;;
+ esac
+ done
+
+ if $mounted_remote_filesystem; then
+ # Cleanup /var again just in case it's a network mount.
+ /etc/rc.d/cleanvar quietreload
+ rm -f /var/run/clean_var /var/spool/lock/clean_var
+
+ # Regenerate the ldconfig hints in case there are additional
+ # library paths on remote file systems
+ /etc/rc.d/ldconfig quietstart
+ fi
+}
+
+load_rc_config $name
+
+# mounting shall not be performed in a svcj
+mountcritremote_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/mountd b/libexec/rc/rc.d/mountd
new file mode 100755
index 000000000000..dfd2431f9c35
--- /dev/null
+++ b/libexec/rc/rc.d/mountd
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: mountd
+# REQUIRE: NETWORKING rpcbind quota mountlate
+# KEYWORD: nojailvnet shutdown
+
+. /etc/rc.subr
+
+name="mountd"
+desc="Service remote NFS mount requests"
+rcvar="mountd_enable"
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+required_files="/etc/exports"
+start_precmd="mountd_precmd"
+extra_commands="reload"
+
+: ${mountd_svcj_options:="net_basic nfsd"}
+
+mountd_precmd()
+{
+
+ # Load the modules now, so that the vfs.nfsd sysctl
+ # oids are available.
+ load_kld nfsd || return 1
+
+ # Do not force rpcbind to be running for an NFSv4 only server.
+ #
+ if checkyesno nfsv4_server_only; then
+ echo 'NFSv4 only server'
+ sysctl vfs.nfsd.server_min_nfsvers=4 > /dev/null
+ sysctl vfs.nfsd.server_max_nfsvers=4 > /dev/null
+ rc_flags="${rc_flags} -R"
+ else
+ force_depend rpcbind || return 1
+ if checkyesno nfsv4_server_enable; then
+ sysctl vfs.nfsd.server_max_nfsvers=4 > /dev/null
+ else
+ sysctl vfs.nfsd.server_max_nfsvers=3 > /dev/null
+ fi
+ fi
+
+ # mountd flags will differ depending on rc.conf settings
+ #
+ if checkyesno nfs_server_enable || checkyesno nfsv4_server_only; then
+ if checkyesno weak_mountd_authentication; then
+ if checkyesno nfsv4_server_only; then
+ echo -n 'weak_mountd_authentication '
+ echo -n 'incompatible with nfsv4_server_only, '
+ echo 'ignored'
+ else
+ rc_flags="${rc_flags} -n"
+ fi
+ fi
+ else
+ if checkyesno mountd_enable; then
+ checkyesno weak_mountd_authentication && rc_flags="-n"
+ fi
+ fi
+
+ if checkyesno zfs_enable; then
+ rc_flags="${rc_flags} /etc/exports /etc/zfs/exports"
+ fi
+
+ rm -f /var/db/mountdtab
+ ( umask 022 ; > /var/db/mountdtab ) ||
+ err 1 'Cannot create /var/db/mountdtab'
+}
+
+load_rc_config $name
+load_rc_config nfsd
+load_rc_config zfs
+
+# precmd is not compatible with svcj
+mountd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/mountlate b/libexec/rc/rc.d/mountlate
new file mode 100755
index 000000000000..87ea9edccb74
--- /dev/null
+++ b/libexec/rc/rc.d/mountlate
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: mountlate
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="mountlate"
+desc="Mount filesystems with \"late\" option from /etc/fstab"
+start_cmd="mountlate_start"
+stop_cmd=":"
+
+mountlate_start()
+{
+ local err latefs
+
+ # Mount "late" filesystems.
+ #
+ err=0
+ echo -n 'Mounting late filesystems:'
+ mount -a -L
+ err=$?
+ echo '.'
+
+ case ${err} in
+ 0)
+ ;;
+ *)
+ echo 'Mounting /etc/fstab filesystems failed,' \
+ 'startup aborted'
+ stop_boot true
+ ;;
+ esac
+
+ # If we booted a special kernel remove the record
+ # so we will boot the default kernel next time.
+ if [ -x /sbin/nextboot ]; then
+ /sbin/nextboot -D
+ fi
+}
+
+load_rc_config $name
+
+# mounting shall not be performed in a svcj
+mountlate_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/moused b/libexec/rc/rc.d/moused
new file mode 100755
index 000000000000..e267ae5b3cd8
--- /dev/null
+++ b/libexec/rc/rc.d/moused
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: moused
+# REQUIRE: DAEMON FILESYSTEMS
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="moused"
+desc="Mouse daemon"
+rcvar="moused_enable"
+command="/usr/sbin/${name}"
+start_cmd="moused_start"
+pidprefix="/var/run/moused"
+pidfile="${pidprefix}.pid"
+pidarg=
+typearg=
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+# XXX: How does moused communiacte with the kernel?
+# XXX: Does the kernel prevent this communcation in jails?
+moused_svcj="NO"
+
+# Set the pid file and variable name. The second argument, if it exists, is
+# expected to be the mouse device.
+#
+if [ -n "$2" ]; then
+ ms=`basename $2`
+ eval moused_${ms}_enable=\${moused_${ms}_enable-${moused_nondefault_enable}}
+ rcvar="moused_${ms}_enable"
+ pidfile="${pidprefix}.${ms}.pid"
+ pidarg="-I $pidfile"
+fi
+
+moused_start()
+{
+ local ms myflags myport mytype
+
+ # Set the mouse device and get any related variables. If
+ # a moused device has been specified on the commandline, then
+ # rc.conf(5) variables defined for that device take precedence
+ # over the generic moused_* variables. The only exception is
+ # the moused_port variable, which if not defined sets it to the
+ # passed in device name.
+ #
+ if [ -n "$1" ]; then
+ ms=`basename $1`
+ eval myflags=\${moused_${ms}_flags-$moused_flags}
+ eval myport=\${moused_${ms}_port-/dev/$1}
+ eval mytype=\${moused_${ms}_type-$moused_type}
+ if [ -n "$mytype" ] && check_kern_features evdev_support; then
+ typearg="-t ${mytype}"
+ fi
+ else
+ ms="default"
+ myflags="$moused_flags"
+ myport="$moused_port"
+ fi
+
+ startmsg -n "Starting ${ms} moused"
+ /usr/sbin/moused ${myflags} -p ${myport} ${typearg} ${pidarg}
+ startmsg '.'
+
+ mousechar_arg=
+ case ${mousechar_start} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ mousechar_arg="-M ${mousechar_start}"
+ ;;
+ esac
+
+ for ttyv in /dev/ttyv* ; do
+ [ "$ttyv" = '/dev/ttyv*' ] && break
+ vidcontrol < ${ttyv} ${mousechar_arg} -m on
+ done
+}
+
+run_rc_command $*
diff --git a/libexec/rc/rc.d/msconvd b/libexec/rc/rc.d/msconvd
new file mode 100755
index 000000000000..c2a96bf2eb68
--- /dev/null
+++ b/libexec/rc/rc.d/msconvd
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: msconvd
+# REQUIRE: DAEMON FILESYSTEMS
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="msconvd"
+desc="Mouse protocol conversion daemon"
+command="/usr/sbin/${name}"
+start_cmd="msconvd_start"
+pidprefix="/var/run/msconvd"
+load_rc_config $name
+
+: ${msconvd_enable="NO"}
+: ${msconvd_type="auto"}
+
+# doesn't make sense to run in a svcj: nojail keyword
+# XXX: How does msconvd communiacte with the kernel?
+# XXX: Does the kernel prevent this communcation in jails?
+msconvd_svcj="NO"
+
+# Set the pid file and variable name. The second argument, if it exists, is
+# expected to be the mouse device.
+#
+if [ -n "$2" ]; then
+ eval msconvd_$2_enable=\${msconvd_$2_enable-${msconvd_enable}}
+ rcvar="msconvd_$2_enable"
+ pidfile="${pidprefix}.$2.pid"
+else
+ for ms in ${msconvd_ports}; do
+ /etc/rc.d/msconvd $1 ${ms}
+ done
+ exit 0
+fi
+
+msconvd_start()
+{
+ local ms myflags myport mytype
+
+ # Set the mouse device and get any related variables. If
+ # a msconvd device has been specified on the commandline, then
+ # rc.conf(5) variables defined for that device take precedence
+ # over the generic msconvd_* variables. The only exception is
+ # the msconvd_port variable, which if not defined sets it to
+ # the passed in device name.
+ #
+ ms=$1
+ eval myflags=\${msconvd_${ms}_flags-$msconvd_flags}
+ eval myport=\${msconvd_${ms}_port-/dev/${ms}}
+ eval mytype=\${msconvd_${ms}_type-$msconvd_type}
+
+ startmsg -n "Starting ${ms} ${name}"
+ ${command} ${myflags} -p ${myport} -t ${mytype} -I ${pidfile}
+ startmsg '.'
+}
+
+run_rc_command $*
diff --git a/libexec/rc/rc.d/msgs b/libexec/rc/rc.d/msgs
new file mode 100755
index 000000000000..424d545f884d
--- /dev/null
+++ b/libexec/rc/rc.d/msgs
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: msgs
+# REQUIRE: LOGIN
+
+. /etc/rc.subr
+
+name="msgs"
+desc="Make a bounds file for msgs(1)"
+start_cmd="msgs_start"
+stop_cmd=":"
+
+msgs_start()
+{
+ # Make a bounds file for msgs(1) if there isn't one already
+ #
+ if [ -d /var/msgs -a ! -f /var/msgs/bounds -a ! -L /var/msgs/bounds ]; then
+ echo 0 > /var/msgs/bounds
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+msgs_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/natd b/libexec/rc/rc.d/natd
new file mode 100755
index 000000000000..1c8c1cb50a96
--- /dev/null
+++ b/libexec/rc/rc.d/natd
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: natd
+# KEYWORD: nostart nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="natd"
+desc="Network Address Translation daemon"
+rcvar="natd_enable"
+command="/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+start_precmd="natd_precmd"
+required_modules="ipdivert"
+
+natd_precmd()
+{
+ if [ -n "${natd_interface}" ]; then
+ dhcp_list="`list_net_interfaces dhcp`"
+ for ifn in ${dhcp_list}; do
+ case "${natd_interface}" in
+ ${ifn})
+ rc_flags="$rc_flags -dynamic"
+ ;;
+ esac
+ done
+
+ if echo "${natd_interface}" | \
+ grep -q -E '^[0-9]+(\.[0-9]+){0,3}$'; then
+ rc_flags="$rc_flags -a ${natd_interface}"
+ else
+ rc_flags="$rc_flags -n ${natd_interface}"
+ fi
+ fi
+
+ return 0
+}
+
+load_rc_config $name
+
+# precmd is not compatible with svcj
+natd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/netif b/libexec/rc/rc.d/netif
new file mode 100755
index 000000000000..8c033acaf828
--- /dev/null
+++ b/libexec/rc/rc.d/netif
@@ -0,0 +1,275 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 The FreeBSD Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE PROJECT BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#
+
+# PROVIDE: netif
+# REQUIRE: FILESYSTEMS iovctl serial sysctl
+# REQUIRE: hostid
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="netif"
+desc="Network interface setup"
+rcvar="${name}_enable"
+start_cmd="netif_start"
+stop_cmd="netif_stop"
+wlanup_cmd="wlan_up"
+wlandown_cmd="wlan_down"
+cloneup_cmd="clone_up"
+clonedown_cmd="clone_down"
+clear_cmd="doclear"
+vnetup_cmd="vnet_up"
+vnetdown_cmd="vnet_down"
+extra_commands="cloneup clonedown clear vnetup vnetdown"
+cmdifn=
+
+set_rcvar_obsolete ipv6_enable ipv6_activate_all_interfaces
+set_rcvar_obsolete ipv6_prefer
+
+netif_start()
+{
+ local _if
+
+ # Set the list of interfaces to work on.
+ #
+ cmdifn=$*
+
+ if [ -z "$cmdifn" ]; then
+ #
+ # We're operating as a general network start routine.
+ #
+
+ # disable SIGINT (Ctrl-c) when running at startup
+ trap : 2
+ fi
+
+ # Create IEEE802.11 interface
+ wlan_up $cmdifn
+
+ # Create cloned interfaces
+ clone_up $cmdifn
+
+ # Rename interfaces.
+ ifnet_rename $cmdifn
+
+ # Configure the interface(s).
+ netif_common ifn_start $cmdifn
+
+ if [ -f /etc/rc.d/ipfilter ] ; then
+ # Resync ipfilter
+ /etc/rc.d/ipfilter quietresync
+ fi
+ if [ -f /etc/rc.d/bridge -a -n "$cmdifn" ] ; then
+ /etc/rc.d/bridge start $cmdifn
+ fi
+ if [ -f /etc/rc.d/routing -a -n "$cmdifn" ] ; then
+ for _if in $cmdifn; do
+ /etc/rc.d/routing static any $_if
+ done
+ fi
+}
+
+netif_stop()
+{
+ _clone_down=1
+ _wlan_down=1
+ netif_stop0 $*
+}
+
+doclear()
+{
+ _clone_down=
+ _wlan_down=
+ netif_stop0 $*
+}
+
+netif_stop0()
+{
+ local _if
+
+ # Set the list of interfaces to work on.
+ #
+ cmdifn=$*
+
+ # Deconfigure the interface(s)
+ netif_common ifn_stop $cmdifn
+
+ # Destroy wlan interfaces
+ if [ -n "$_wlan_down" ]; then
+ wlan_down $cmdifn
+ fi
+
+ # Destroy cloned interfaces
+ if [ -n "$_clone_down" ]; then
+ clone_down $cmdifn
+ fi
+
+ if [ -f /etc/rc.d/routing -a -n "$cmdifn" ] ; then
+ for _if in $cmdifn; do
+ /etc/rc.d/routing stop any $_if
+ done
+ fi
+}
+
+vnet_up()
+{
+ cmdifn=$*
+
+ netif_common ifn_vnetup $cmdifn
+}
+
+vnet_down()
+{
+ cmdifn=$*
+
+ netif_common ifn_vnetdown $cmdifn
+}
+
+# netif_common routine
+# Common configuration subroutine for network interfaces. This
+# routine takes all the preparatory steps needed for configuring
+# an interface and then calls $routine.
+netif_common()
+{
+ local _cooked_list _tmp_list _fail _func _ok _str _cmdifn
+
+ _func=
+
+ if [ -z "$1" ]; then
+ err 1 "netif_common(): No function name specified."
+ else
+ _func="$1"
+ shift
+ fi
+
+ # Set the scope of the command (all interfaces or just one).
+ #
+ _cooked_list=
+ _tmp_list=
+ _cmdifn=$*
+ if [ -n "$_cmdifn" ]; then
+ # Don't check that the interface(s) exist. We need to run
+ # the down code even when the interface doesn't exist to
+ # kill off wpa_supplicant.
+ # XXXBED: is this really true or does wpa_supplicant die?
+ # if so, we should get rid of the devd entry
+ _cooked_list="$_cmdifn"
+ else
+ _cooked_list="`list_net_interfaces`"
+ fi
+
+ # Expand epair[0-9] to epair[0-9][ab].
+ for ifn in $_cooked_list; do
+ case ${ifn#epair} in
+ [0-9]*[ab]) ;; # Skip epair[0-9]*[ab].
+ [0-9]*)
+ for _str in $_cooked_list; do
+ case $_str in
+ $ifn) _tmp_list="$_tmp_list ${ifn}a ${ifn}b" ;;
+ *) _tmp_list="$_tmp_list ${ifn}" ;;
+ esac
+ done
+ _cooked_list=${_tmp_list# }
+ ;;
+ esac
+ done
+
+ _dadwait=
+ _fail=
+ _ok=
+ for ifn in ${_cooked_list# }; do
+ # Skip if ifn does not exist.
+ case $_func in
+ ifn_stop)
+ if ! ${IFCONFIG_CMD} $ifn > /dev/null 2>&1; then
+ warn "$ifn does not exist. Skipped."
+ _fail="${_fail} ${ifn}"
+ continue
+ fi
+ ;;
+ esac
+ if ${_func} ${ifn} $2; then
+ _ok="${_ok} ${ifn}"
+ if ipv6if ${ifn} && [ "${ifn}" != "lo0" ]; then
+ _dadwait=1
+ fi
+ else
+ _fail="${_fail} ${ifn}"
+ fi
+ done
+
+ # inet6 address configuration needs sleep for DAD.
+ case ${_func}:${_dadwait} in
+ ifn_start:1|ifn_vnetup:1|ifn_vnetdown:1)
+ sleep `${SYSCTL_N} net.inet6.ip6.dad_count`
+ sleep 1
+ ;;
+ esac
+
+ _str=
+ if [ -n "${_ok}" ]; then
+ case ${_func} in
+ ifn_start)
+ _str='Starting'
+ ;;
+ ifn_stop)
+ _str='Stopping'
+ ;;
+ ifn_vnetup)
+ _str='Moving'
+ ;;
+ ifn_vnetdown)
+ _str='Reclaiming'
+ ;;
+ esac
+ startmsg "${_str} Network:${_ok}."
+ case ${_func} in
+ ifn_vnetup)
+ # Clear _ok not to do "ifconfig $ifn"
+ # because $ifn is no longer in the current vnet.
+ _ok=
+ ;;
+ esac
+ if check_startmsgs; then
+ for ifn in ${_ok}; do
+ /sbin/ifconfig ${ifn}
+ done
+ fi
+ fi
+
+ debug "The following interfaces were not configured: $_fail"
+}
+
+# Load the old "network" config file also for compatibility.
+# This is needed for mfsBSD at least.
+load_rc_config network
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+netif_svcj="NO"
+
+run_rc_command $*
diff --git a/libexec/rc/rc.d/netoptions b/libexec/rc/rc.d/netoptions
new file mode 100755
index 000000000000..0f329a5385cf
--- /dev/null
+++ b/libexec/rc/rc.d/netoptions
@@ -0,0 +1,129 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: netoptions
+# REQUIRE: FILESYSTEMS
+# BEFORE: netif
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="netoptions"
+desc="Network options setup"
+start_cmd="netoptions_start"
+stop_cmd=:
+
+_netoptions_initdone=
+netoptions_init()
+{
+ if [ -z "${_netoptions_initdone}" ]; then
+ echo -n 'Additional TCP/IP options:'
+ _netoptions_initdone=yes
+ fi
+}
+
+netoptions_start()
+{
+ local _af
+
+ for _af in inet inet6; do
+ afexists ${_af} && eval netoptions_${_af}
+ done
+ [ -n "${_netoptions_initdone}" ] && echo '.'
+}
+
+netoptions_inet()
+{
+ case ${log_in_vain} in
+ [12])
+ netoptions_init
+ echo -n " log_in_vain=${log_in_vain}"
+ ${SYSCTL} net.inet.tcp.log_in_vain=${log_in_vain} >/dev/null
+ ${SYSCTL} net.inet.udp.log_in_vain=${log_in_vain} >/dev/null
+ ;;
+ *)
+ ${SYSCTL} net.inet.tcp.log_in_vain=0 >/dev/null
+ ${SYSCTL} net.inet.udp.log_in_vain=0 >/dev/null
+ ;;
+ esac
+
+ if checkyesno tcp_extensions; then
+ ${SYSCTL} net.inet.tcp.rfc1323=1 >/dev/null
+ else
+ netoptions_init
+ echo -n " rfc1323 extensions=${tcp_extensions}"
+ ${SYSCTL} net.inet.tcp.rfc1323=0 >/dev/null
+ fi
+
+ if checkyesno tcp_keepalive; then
+ ${SYSCTL} net.inet.tcp.always_keepalive=1 >/dev/null
+ else
+ netoptions_init
+ echo -n " TCP keepalive=${tcp_keepalive}"
+ ${SYSCTL} net.inet.tcp.always_keepalive=0 >/dev/null
+ fi
+
+ if checkyesno tcp_drop_synfin; then
+ netoptions_init
+ echo -n " drop SYN+FIN packets=${tcp_drop_synfin}"
+ ${SYSCTL} net.inet.tcp.drop_synfin=1 >/dev/null
+ else
+ ${SYSCTL} net.inet.tcp.drop_synfin=0 >/dev/null
+ fi
+
+ case ${ip_portrange_first} in
+ [0-9]*)
+ netoptions_init
+ echo -n " ip_portrange_first=$ip_portrange_first"
+ ${SYSCTL} net.inet.ip.portrange.first=$ip_portrange_first >/dev/null
+ ;;
+ esac
+
+ case ${ip_portrange_last} in
+ [0-9]*)
+ netoptions_init
+ echo -n " ip_portrange_last=$ip_portrange_last"
+ ${SYSCTL} net.inet.ip.portrange.last=$ip_portrange_last >/dev/null
+ ;;
+ esac
+}
+
+netoptions_inet6()
+{
+ if checkyesno ipv6_ipv4mapping; then
+ netoptions_init
+ echo -n " ipv4-mapped-ipv6=${ipv6_ipv4mapping}"
+ ${SYSCTL} net.inet6.ip6.v6only=0 >/dev/null
+ else
+ ${SYSCTL} net.inet6.ip6.v6only=1 >/dev/null
+ fi
+
+ if checkyesno ipv6_privacy; then
+ netoptions_init
+ echo -n " IPv6 Privacy Addresses"
+ ${SYSCTL} net.inet6.ip6.use_tempaddr=1 >/dev/null
+ ${SYSCTL} net.inet6.ip6.prefer_tempaddr=1 >/dev/null
+ fi
+
+ case $ipv6_cpe_wanif in
+ ""|[Nn][Oo]|[Nn][Oo][Nn][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
+ ${SYSCTL} net.inet6.ip6.no_radr=0 >/dev/null
+ ${SYSCTL} net.inet6.ip6.rfc6204w3=0 >/dev/null
+ ;;
+ *)
+ netoptions_init
+ echo -n " IPv6 CPE WANIF=${ipv6_cpe_wanif}"
+ ${SYSCTL} net.inet6.ip6.no_radr=1 >/dev/null
+ ${SYSCTL} net.inet6.ip6.rfc6204w3=1 >/dev/null
+ ;;
+ esac
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+netoptions_svcj="NO"
+
+run_rc_command $1
diff --git a/libexec/rc/rc.d/netwait b/libexec/rc/rc.d/netwait
new file mode 100755
index 000000000000..05874552cf1c
--- /dev/null
+++ b/libexec/rc/rc.d/netwait
@@ -0,0 +1,156 @@
+#!/bin/sh
+#
+# PROVIDE: netwait
+# REQUIRE: devd ipfw pf routing
+#
+# The netwait script helps handle three situations:
+# - Systems with USB or other late-attaching network hardware which
+# is initialized by devd events. The script waits for all the
+# interfaces named in the netwait_if list to appear.
+# - Systems with IPv6 addresses, especially jails, where we need to
+# wait for DAD to complete before starting daemons, as they will
+# otherwise fail to bind to IN6ADDR_ANY.
+# - Systems with statically-configured IP addresses in rc.conf(5).
+# The IP addresses in the netwait_ip list are pinged. The script
+# waits for any single IP in the list to respond to the ping. If your
+# system uses DHCP, you should probably use synchronous_dhclient="YES"
+# in your /etc/rc.conf instead of netwait_ip.
+# Either or both of the wait lists can be used (at least one must be
+# non-empty if netwait is enabled).
+
+. /etc/rc.subr
+
+name="netwait"
+desc="Wait for network devices or the network being up"
+rcvar="netwait_enable"
+
+start_cmd="${name}_start"
+stop_cmd=":"
+
+netwait_start()
+{
+ local ip rc count output link wait_if got_if any_error
+
+ if [ -z "${netwait_if}" ] && [ -z "${netwait_ip}" ] &&
+ ! checkyesno netwait_dad ; then
+ err 1 "Nothing to wait for"
+ fi
+
+ if ! [ "${netwait_if_timeout:=0}" -ge 1 ]; then
+ err 1 "netwait_if_timeout must be >= 1"
+ fi
+ if ! check_kern_features inet6; then
+ netwait_dad="NO"
+ elif ! [ "${netwait_dad_timeout:=0}" -ge 1 ]; then
+ netwait_dad_timeout=$(($(sysctl -n net.inet6.ip6.dad_count)+1))
+ fi
+ if ! [ "${netwait_timeout:=0}" -ge 1 ]; then
+ err 1 "netwait_timeout must be >= 1"
+ fi
+
+ any_error=false
+
+ if [ -n "${netwait_if}" ]; then
+ for wait_if in ${netwait_if}; do
+ echo -n "Waiting for ${wait_if}"
+ link=""
+ got_if=false
+ count=1
+ # Handle SIGINT (Ctrl-C); force abort of while loop
+ trap break SIGINT
+ while [ ${count} -le ${netwait_if_timeout} ]; do
+ if output=`/sbin/ifconfig ${wait_if} 2>/dev/null`; then
+ if ! ${got_if}; then
+ echo -n ", interface present"
+ got_if=true
+ fi
+ link=`expr "${output}" : '.*[[:blank:]]status: \(no carrier\)'`
+ if [ -z "${link}" ]; then
+ echo ', got link.'
+ break
+ fi
+ fi
+ sleep 1
+ count=$((count+1))
+ done
+ # Restore default SIGINT handler
+ trap - SIGINT
+ if ! ${got_if}; then
+ echo ", wait failed: interface never appeared."
+ any_error=true
+ elif [ -n "${link}" ]; then
+ echo ", wait failed: interface still has no link."
+ any_error=true
+ fi
+ done
+ fi
+
+ if checkyesno netwait_dad; then
+ got_dad=false
+ # Handle SIGINT (Ctrl-C); force abort of while loop
+ trap break SIGINT
+
+ echo -n "Waiting for DAD to complete"
+ count=1
+ while [ ${count} -le ${netwait_dad_timeout} ]; do
+ if ! ifconfig | grep -q 'inet6.*tentative'; then
+ echo ', done.'
+ got_dad=true
+ break
+ fi
+ sleep 1
+ count=$((count+1))
+ done
+
+ # Restore default SIGINT handler
+ trap - SIGINT
+
+ if ! ${got_dad}; then
+ echo ', timed out.'
+ any_error=true
+ fi
+ fi
+
+ if [ -n "${netwait_ip}" ]; then
+ got_ip=false
+ # Handle SIGINT (Ctrl-C); force abort of for loop
+ trap break SIGINT
+
+ for ip in ${netwait_ip}; do
+ echo -n "Waiting for ${ip} to respond to ICMP ping"
+
+ count=1
+ while [ ${count} -le ${netwait_timeout} ]; do
+ /sbin/ping -t 1 -c 1 -o ${ip} >/dev/null 2>&1
+ rc=$?
+
+ if [ $rc -eq 0 ]; then
+ echo ', got response.'
+ got_ip=false
+ break 2
+ fi
+ count=$((count+1))
+ done
+ echo ', failed: No response from host.'
+ done
+
+ # Restore default SIGINT handler
+ trap - SIGINT
+
+ if ! ${got_ip}; then
+ any_error=true
+ fi
+ fi
+
+ if ${any_error}; then
+ warn "Continuing with startup, but be aware you may not have "
+ warn "a fully functional networking layer at this point."
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+netwait_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/newsyslog b/libexec/rc/rc.d/newsyslog
new file mode 100755
index 000000000000..9b959bfabe85
--- /dev/null
+++ b/libexec/rc/rc.d/newsyslog
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: newsyslog
+# REQUIRE: FILESYSTEMS mountcritremote
+
+. /etc/rc.subr
+
+name="newsyslog"
+desc="Logfile rotation"
+rcvar="newsyslog_enable"
+required_files="/etc/newsyslog.conf"
+command="/usr/sbin/${name}"
+start_cmd="newsyslog_start"
+stop_cmd=":"
+
+newsyslog_start()
+{
+ startmsg -n 'Creating and/or trimming log files'
+ ${command} ${rc_flags}
+ startmsg '.'
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: needs to send signals outside the svcj
+newsyslog_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/nfscbd b/libexec/rc/rc.d/nfscbd
new file mode 100755
index 000000000000..450de46e0855
--- /dev/null
+++ b/libexec/rc/rc.d/nfscbd
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: nfscbd
+# REQUIRE: NETWORKING nfsuserd
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="nfscbd"
+desc="NFSv4 client side callback daemon"
+rcvar="nfscbd_enable"
+command="/usr/sbin/${name}"
+sig_stop="USR1"
+
+: ${nfscbd_svcj_options:="net_basic"}
+
+load_rc_config $name
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/nfsclient b/libexec/rc/rc.d/nfsclient
new file mode 100755
index 000000000000..857cfa02036f
--- /dev/null
+++ b/libexec/rc/rc.d/nfsclient
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: nfsclient
+# REQUIRE: NETWORKING mountcritremote rpcbind
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="nfsclient"
+desc="NFS client setup"
+rcvar="nfs_client_enable"
+start_cmd="nfsclient_start"
+stop_cmd="unmount_all"
+required_modules="nfscl:nfs"
+
+nfsclient_start()
+{
+ #
+ # Set some nfs client related sysctls
+ #
+
+ if [ -n "${nfs_access_cache}" ]; then
+ startmsg "NFS access cache time=${nfs_access_cache}"
+ if ! sysctl vfs.nfs.access_cache_timeout=${nfs_access_cache} >/dev/null; then
+ warn "failed to set access cache timeout"
+ fi
+ fi
+ if [ -n "${nfs_bufpackets}" ]; then
+ if ! sysctl vfs.nfs.bufpackets=${nfs_bufpackets} > /dev/null; then
+ warn "failed to set vfs.nfs.bufpackets"
+ fi
+ fi
+
+ unmount_all
+}
+
+unmount_all()
+{
+ # If /var/db/mounttab exists, some nfs-server has not been
+ # successfully notified about a previous client shutdown.
+ # If there is no /var/db/mounttab, we do nothing.
+ if [ -f /var/db/mounttab ]; then
+ rpc.umntall -k
+ fi
+}
+load_rc_config $name
+
+# no unmounting in svcj
+nfsclient_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/nfsd b/libexec/rc/rc.d/nfsd
new file mode 100755
index 000000000000..364c2a3b6bd3
--- /dev/null
+++ b/libexec/rc/rc.d/nfsd
@@ -0,0 +1,68 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: nfsd
+# REQUIRE: mountcritremote mountd hostname gssd nfsuserd
+# KEYWORD: nojailvnet shutdown
+
+. /etc/rc.subr
+
+name="nfsd"
+desc="Remote NFS server"
+rcvar="nfs_server_enable"
+command="/usr/sbin/${name}"
+nfs_server_vhost=""
+
+: ${nfsd_svcj_options:="net_basic nfsd"}
+
+load_rc_config $name
+# precmd is not compatible with svcj
+nfsd_svcj="NO"
+start_precmd="nfsd_precmd"
+sig_stop="USR1"
+
+nfsd_precmd()
+{
+ local _vhost
+ rc_flags="${nfs_server_flags}"
+
+ # Load the modules now, so that the vfs.nfsd sysctl
+ # oids are available.
+ load_kld nfsd || return 1
+
+ if [ -n "${nfs_server_maxio}" ] && ! check_jail jailed; then
+ if ! sysctl vfs.nfsd.srvmaxio=${nfs_server_maxio} >/dev/null; then
+ warn "Failed to set server max I/O"
+ fi
+ fi
+
+ if checkyesno nfs_reserved_port_only; then
+ echo 'NFS on reserved port only=YES'
+ sysctl vfs.nfsd.nfs_privport=1 > /dev/null
+ else
+ sysctl vfs.nfsd.nfs_privport=0 > /dev/null
+ fi
+
+ if checkyesno nfs_server_managegids; then
+ force_depend nfsuserd || err 1 "Cannot run nfsuserd"
+ fi
+
+ if checkyesno nfsv4_server_enable; then
+ sysctl vfs.nfsd.server_max_nfsvers=4 > /dev/null
+ elif ! checkyesno nfsv4_server_only; then
+ echo 'NFSv4 is disabled'
+ sysctl vfs.nfsd.server_max_nfsvers=3 > /dev/null
+ fi
+
+ if ! checkyesno nfsv4_server_only; then
+ force_depend rpcbind || return 1
+ fi
+
+ force_depend mountd || return 1
+ if [ -n "${nfs_server_vhost}" ]; then
+ command_args="-V \"${nfs_server_vhost}\""
+ fi
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/nfsuserd b/libexec/rc/rc.d/nfsuserd
new file mode 100755
index 000000000000..3ef88dcc6dfc
--- /dev/null
+++ b/libexec/rc/rc.d/nfsuserd
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: nfsuserd
+# REQUIRE: NETWORKING
+# KEYWORD: nojailvnet shutdown
+
+. /etc/rc.subr
+
+name="nfsuserd"
+desc="Load user and group information into the kernel for NFSv4 services and support manage-gids for all NFS versions"
+rcvar="nfsuserd_enable"
+command="/usr/sbin/${name}"
+sig_stop="USR1"
+
+: ${nfsuserd_svcj_options:="net_basic nfsd"}
+
+load_rc_config $name
+# precmd is not compatible with svcj
+nfsuserd_svcj="NO"
+start_precmd="nfsuserd_precmd"
+
+nfsuserd_precmd()
+{
+ if checkyesno nfs_server_managegids; then
+ rc_flags="-manage-gids ${nfsuserd_flags}"
+ fi
+ return 0
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/nisdomain b/libexec/rc/rc.d/nisdomain
new file mode 100755
index 000000000000..9616d7be39ac
--- /dev/null
+++ b/libexec/rc/rc.d/nisdomain
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Copyright (c) 1993 - 2003 The FreeBSD Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: nisdomain
+# REQUIRE: SERVERS rpcbind
+# BEFORE: ypset ypbind ypserv ypxfrd
+
+. /etc/rc.subr
+
+name="nisdomain"
+desc="Set NIS domain name"
+start_cmd="nisdomain_start"
+stop_cmd=":"
+
+nisdomain_start()
+{
+ # Set the domainname if we're using NIS
+ #
+ case ${nisdomainname} in
+ [Nn][Oo]|'')
+ ;;
+ *)
+ domainname ${nisdomainname}
+ echo "Setting NIS domain: `/bin/domainname`."
+ ;;
+ esac
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+nisdomain_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/noshutdown b/libexec/rc/rc.d/noshutdown
new file mode 100755
index 000000000000..54924310a6c7
--- /dev/null
+++ b/libexec/rc/rc.d/noshutdown
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: noshutdown
+# REQUIRE: var
+# BEFORE: LOGIN
+
+. /etc/rc.subr
+
+name="noshutdown"
+desc="Disable shutdown(8) for precious machines"
+rcvar="precious_machine"
+start_cmd="noshutdown_start"
+stop_cmd="noshutdown_stop"
+
+: ${noshutdown_file:="/var/run/noshutdown"}
+
+noshutdown_start()
+{
+ touch $noshutdown_file
+}
+
+noshutdown_stop()
+{
+ rm -f $noshutdown_file
+}
+
+load_rc_config $name
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/nscd b/libexec/rc/rc.d/nscd
new file mode 100755
index 000000000000..611d2d8ddb8f
--- /dev/null
+++ b/libexec/rc/rc.d/nscd
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: nscd
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+#
+# Add the following lines to /etc/rc.conf to enable nscd:
+#
+# nscd_enable="YES"
+#
+# See nscd(8) for flags
+#
+
+. /etc/rc.subr
+
+name="nscd"
+desc="Name-service caching daemon"
+rcvar="nscd_enable"
+
+# no svcj options needed
+: ${nscd_svcj_options:=""}
+
+command=/usr/sbin/nscd
+extra_commands="flush"
+flush_cmd="${command} -I all"
+
+# usage: _nscd_set_option <option name> <default value>
+#
+_nscd_set_option() {
+ local _optname _defoptval _nscd_opt_val _cached_opt_val
+ _optname=$1
+ _defoptval=$2
+
+ _nscd_opt_val=$(eval "echo \$nscd_${_optname}")
+ _cached_opt_val=$(eval "echo \$cached_${_optname}")
+
+ if [ -n "$_cached_opt_val" -a "$_nscd_opt_val" != "$_defoptval" ]; then
+ warn "You should use nscd_${_optname} instead of" \
+ "cached_${_optname}"
+ setvar "nscd_${_optname}" "$_cached_opt_val"
+ else
+ setvar "nscd_${_optname}" "${_nscd_opt_val:-$_defoptval}"
+ fi
+}
+
+
+load_rc_config $name
+_nscd_set_option "enable" "NO"
+_nscd_set_option "pidfile" "/var/run/nscd.pid"
+_nscd_set_option "flags" ""
+run_rc_command "$1"
+
diff --git a/libexec/rc/rc.d/ntpd b/libexec/rc/rc.d/ntpd
new file mode 100755
index 000000000000..e7e42da8acc7
--- /dev/null
+++ b/libexec/rc/rc.d/ntpd
@@ -0,0 +1,250 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ntpd
+# REQUIRE: DAEMON ntpdate FILESYSTEMS devfs
+# BEFORE: LOGIN
+# KEYWORD: nojail resume shutdown
+
+. /etc/rc.subr
+
+name="ntpd"
+desc="Network Time Protocol daemon"
+rcvar="ntpd_enable"
+command="/usr/sbin/${name}"
+extra_commands="fetch needfetch resume"
+fetch_cmd="ntpd_fetch_leapfile"
+needfetch_cmd="ntpd_needfetch_leapfile"
+resume_cmd="ntpd_resume"
+start_precmd="ntpd_precmd"
+
+_ntp_tmp_leapfile="/var/run/ntpd.leap-seconds.list"
+_ntp_default_dir="/var/db/ntp"
+_ntp_default_driftfile="${_ntp_default_dir}/ntpd.drift"
+_ntp_old_driftfile="/var/db/ntpd.drift"
+
+pidfile="${_ntp_default_dir}/${name}.pid"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+ntpd_svcj="NO"
+
+leapfile_is_disabled() {
+ # Return true (0) if automatic leapfile handling is disabled.
+ case "$ntp_db_leapfile" in
+ [Nn][Oo] | [Nn][Oo][Nn][Ee] )
+ return 0;;
+ * )
+ return 1;;
+ esac
+}
+
+can_run_nonroot()
+{
+ # If the admin set what uid to use, we don't change it.
+ if [ -n "${ntpd_user}" ]; then
+ return 1
+ fi
+
+ # If the admin set any command line options involving files, we
+ # may not be able to access them as user ntpd.
+ case "${rc_flags}" in
+ *-f* | *--driftfile* | *-i* | *--jaildir* | \
+ *-k* | *--keyfile* | *-l* | *--logfile* | \
+ *-p* | *--pidfile* | *-s* | *--statsdir* )
+ return 1;;
+ esac
+
+ # If the admin set any options in ntp.conf involving files,
+ # we may not be able to access them as user ntpd.
+ local fileopts="^[ \t]*crypto|^[ \t]*driftfile|^[ \t]*key|^[ \t]*logfile|^[ \t]*statsdir"
+ grep -E -q "${fileopts}" "${ntpd_config}" && return 1
+
+ # Try to set up the MAC ntpd policy so ntpd can run with reduced
+ # privileges. Detect whether MAC is compiled into the kernel, load
+ # the policy module if not already present, then check whether the
+ # policy has been disabled via tunable or sysctl.
+ [ -n "$(sysctl -qn security.mac.version)" ] || return 1
+ sysctl -qn security.mac.ntpd >/dev/null || kldload -qn mac_ntpd || return 1
+ [ "$(sysctl -qn security.mac.ntpd.enabled)" == "1" ] || return 1
+
+ # On older existing systems, the ntp dir may by owned by root, change
+ # it to ntpd to give the daemon create/write access to the driftfile.
+ if [ "$(stat -f %u ${_ntp_default_dir})" = "0" ]; then
+ chown ntpd:ntpd "${_ntp_default_dir}" || return 1
+ chmod 0755 "${_ntp_default_dir}" || return 1
+ logger -s -t "rc.d/ntpd" -p daemon.notice \
+ "${_ntp_default_dir} updated to owner ntpd:ntpd, mode 0755"
+ fi
+
+ # If the driftfile exists in the standard location for older existing
+ # systems, move it into the ntp dir and fix the ownership if we can.
+ if [ -f "${_ntp_old_driftfile}" ] && [ ! -L "${_ntp_old_driftfile}" ]; then
+ mv "${_ntp_old_driftfile}" "${_ntp_default_driftfile}" &&
+ chown ntpd:ntpd "${_ntp_default_driftfile}" || return 1
+ logger -s -t "rc.d/ntpd" -p daemon.notice \
+ "${_ntp_default_driftfile} updated to owner ntpd:ntpd"
+ logger -s -t "rc.d/ntpd" -p daemon.notice \
+ "${_ntp_old_driftfile} moved to ${_ntp_default_driftfile}"
+ fi
+}
+
+ntpd_precmd()
+{
+ local driftopt
+
+ # If we can run as a non-root user, switch uid to ntpd and use the
+ # new default location for the driftfile inside the ntpd-owned dir.
+ # Otherwise, figure out what to do about the driftfile option. If set
+ # by the admin, we don't add the option. If the file exists in the old
+ # default location we use that, else we use the new default location.
+ if can_run_nonroot; then
+ _user="ntpd"
+ driftopt="-f ${_ntp_default_driftfile}"
+ elif grep -q "^[ \t]*driftfile" "${ntpd_config}" ||
+ [ -n "${rc_flags}" ] &&
+ ( [ -z "${rc_flags##*-f*}" ] ||
+ [ -z "${rc_flags##*--driftfile*}" ] ); then
+ driftopt="" # admin set the option, we don't need to add it.
+ elif [ -f "${_ntp_old_driftfile}" ]; then
+ driftopt="-f ${_ntp_old_driftfile}"
+ else
+ driftopt="-f ${_ntp_default_driftfile}"
+ fi
+
+ # Set command_args based on the various config vars.
+ command_args="-p ${pidfile} -c ${ntpd_config} ${driftopt}"
+ if checkyesno ntpd_sync_on_start; then
+ command_args="${command_args} -g"
+ fi
+
+ # Make sure the leapfile is ready to use, unless leapfile
+ # handling is disabled.
+ if leapfile_is_disabled; then
+ return
+ fi
+
+ ntpd_init_leapfile
+ if [ ! -f "${ntp_db_leapfile}" ]; then
+ ntpd_fetch_leapfile
+ fi
+}
+
+current_ntp_ts() {
+ # Seconds between 1900-01-01 and 1970-01-01
+ # echo $(((70*365+17)*86400))
+ ntp_to_unix=2208988800
+
+ echo $(($(date -u +%s)+$ntp_to_unix))
+}
+
+get_ntp_leapfile_ver() {
+ # Leapfile update date (version number).
+ expr "$(awk '$1 == "#$" { print $2 }' "$1" 2>/dev/null)" : \
+ '^\([1-9][0-9]*\)$' \| 0
+}
+
+get_ntp_leapfile_expiry() {
+ # Leapfile expiry date.
+ expr "$(awk '$1 == "#@" { print $2 }' "$1" 2>/dev/null)" : \
+ '^\([1-9][0-9]*\)$' \| 0
+}
+
+ntpd_init_leapfile() {
+
+ if leapfile_is_disabled; then
+ return
+ fi
+
+ # Refresh working leapfile with an invalid hash due to
+ # FreeBSD id header. Ntpd will ignore leapfiles with a
+ # mismatch hash. The file must be the virgin file from
+ # the source.
+ if [ ! -f $ntp_db_leapfile ]; then
+ cp -p $ntp_src_leapfile $ntp_db_leapfile
+ fi
+}
+
+ntpd_needfetch_leapfile() {
+ local rc verbose
+
+ if leapfile_is_disabled; then
+ # Return code 1: ntp leapfile fetch not needed
+ return 1
+ fi
+
+ if checkyesno ntp_leapfile_fetch_verbose; then
+ verbose=echo
+ else
+ verbose=:
+ fi
+
+ ntp_ver_no_src=$(get_ntp_leapfile_ver $ntp_src_leapfile)
+ ntp_expiry_src=$(get_ntp_leapfile_expiry $ntp_src_leapfile)
+ ntp_ver_no_db=$(get_ntp_leapfile_ver $ntp_db_leapfile)
+ ntp_expiry_db=$(get_ntp_leapfile_expiry $ntp_db_leapfile)
+ $verbose ntp_src_leapfile version is $ntp_ver_no_src expires $ntp_expiry_src
+ $verbose ntp_db_leapfile version is $ntp_ver_no_db expires $ntp_expiry_db
+
+ if [ "$ntp_ver_no_src" -gt "$ntp_ver_no_db" -o \
+ "$ntp_ver_no_src" -eq "$ntp_ver_no_db" -a \
+ "$ntp_expiry_src" -gt "$ntp_expiry_db" ]; then
+ $verbose replacing $ntp_db_leapfile with $ntp_src_leapfile
+ cp -p $ntp_src_leapfile $ntp_db_leapfile
+ ntp_ver_no_db=$ntp_ver_no_src
+ else
+ $verbose not replacing $ntp_db_leapfile with $ntp_src_leapfile
+ fi
+ ntp_leapfile_expiry_seconds=$((ntp_leapfile_expiry_days*86400))
+ ntp_leap_expiry=$(get_ntp_leapfile_expiry $ntp_db_leapfile)
+ ntp_leap_fetch_date=$((ntp_leap_expiry-ntp_leapfile_expiry_seconds))
+ if [ $(current_ntp_ts) -ge $ntp_leap_fetch_date ]; then
+ $verbose Within ntp leapfile expiry limit, initiating fetch
+ # Return code 0: ntp leapfile fetch needed
+ return 0
+ fi
+ # Return code 1: ntp leapfile fetch not needed
+ return 1
+}
+
+ntpd_fetch_leapfile() {
+
+ if leapfile_is_disabled; then
+ return
+ fi
+
+ if checkyesno ntp_leapfile_fetch_verbose; then
+ verbose=echo
+ else
+ verbose=:
+ fi
+
+ if ntpd_needfetch_leapfile ; then
+ for url in $ntp_leapfile_sources ; do
+ $verbose fetching $url
+ # Circumvent umask 027 and 077 in login.conf(5)
+ umask 022
+ fetch $ntp_leapfile_fetch_opts -o $_ntp_tmp_leapfile $url && break
+ done
+ ntp_ver_no_tmp=$(get_ntp_leapfile_ver $_ntp_tmp_leapfile)
+ ntp_expiry_tmp=$(get_ntp_leapfile_expiry $_ntp_tmp_leapfile)
+ if [ "$ntp_expiry_tmp" -gt "$ntp_expiry_db" -o \
+ "$ntp_expiry_tmp" -eq "$ntp_expiry_db" -a \
+ "$ntp_ver_no_tmp" -gt "$ntp_ver_no_db" ]; then
+ $verbose using $url as $ntp_db_leapfile
+ mv -f $_ntp_tmp_leapfile $ntp_db_leapfile ||
+ $verbose "warning: cannot replace $ntp_db_leapfile (read-only fs?)"
+ else
+ $verbose using existing $ntp_db_leapfile
+ fi
+ fi
+}
+
+ntpd_resume()
+{
+ run_rc_command restart
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ntpdate b/libexec/rc/rc.d/ntpdate
new file mode 100755
index 000000000000..cb948d739227
--- /dev/null
+++ b/libexec/rc/rc.d/ntpdate
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ntpdate
+# REQUIRE: NETWORKING syslogd
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="ntpdate"
+desc="Set the date and time via NTP"
+rcvar="ntpdate_enable"
+stop_cmd=":"
+start_cmd="ntpdate_start"
+
+ntpdate_start()
+{
+ if [ -z "$ntpdate_hosts" -a -f "$ntpdate_config" ]; then
+ ntpdate_hosts=`awk '
+ /^server[ \t]*127.127/ {next}
+ /^(server|peer|pool)/ {
+ if ($2 ~/^-/) {print $3}
+ else {print $2}}
+ ' < "$ntpdate_config"`
+ fi
+ if [ -n "$ntpdate_hosts" -o -n "$rc_flags" ]; then
+ echo "Setting date via ntp."
+ ${ntpdate_program:-ntpdate} $rc_flags $ntpdate_hosts
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: privileged operations
+ntpdate_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/nuageinit b/libexec/rc/rc.d/nuageinit
new file mode 100755
index 000000000000..c901971488bd
--- /dev/null
+++ b/libexec/rc/rc.d/nuageinit
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+
+# PROVIDE: nuageinit
+# REQUIRE: mountcritlocal zfs devmatch
+# BEFORE: NETWORKING
+# KEYWORD: firstboot
+
+. /etc/rc.subr
+
+name="nuageinit"
+desc="Limited Cloud Init configuration"
+start_cmd="nuageinit_start"
+stop_cmd=":"
+rcvar="nuageinit_enable"
+
+fetch_openstack()
+{
+ cd /media/nuageinit/openstack/latest
+ for file in meta_data.json network_data.json user_data; do
+ fetch http://169.254.169.254/openstack/latest/$file || :
+ done
+ if [ -f user_data ]; then
+ chmod 755 user_data
+ fi
+ cd -
+}
+
+nuageinit_start()
+{
+ local citype
+ # detect cloud init provider
+ # according to the specification, the config drive
+ # is either formatted in vfat or iso9660 and labeled
+ # config-2
+ for f in iso9660 msdosfs; do
+ drive="/dev/$f/[cC][oO][nN][fF][iI][gG]-2"
+ if [ -e $drive ]; then
+ citype=config-2
+ break
+ fi
+ drive="/dev/$f/[cC][iI][dD][aA][tT][aA]"
+ if [ -e $drive ]; then
+ citype=nocloud
+ break
+ fi
+ unset drive
+ done
+ if [ -n "$drive" ]; then
+ mkdir -p /media/nuageinit
+ fs=$(fstyp $drive 2> /dev/null)
+ mount -t $fs $drive /media/nuageinit
+ else
+ product=$(kenv smbios.system.product)
+ case "$product" in
+ OpenStack*)
+ mkdir -p /media/nuageinit/openstack/latest
+ ifaces=$(ifconfig -l ether)
+ set -- $ifaces
+ dhclient -p /tmp/ephemeraldhcp.pid $1
+ fetch_openstack
+ pkill -F /tmp/ephemeraldhcp.pid
+ citype=config-2
+ ;;
+ *)
+ # try to detect networked based instance
+ err 1 "Impossible to find a cloud init provider"
+ ;;
+ esac
+ fi
+ # according to the specification, the content is either
+ # in the openstack or ec2 directory
+ case "$citype" in
+ config-2)
+ for d in openstack ec2; do
+ dir=/media/nuageinit/$d/latest
+ if [ -d $dir ]; then
+ /usr/libexec/nuageinit $dir $citype 2>&1 | tee -a /var/log/nuageinit.log
+ break
+ fi
+ done
+ ;;
+ nocloud)
+ /usr/libexec/nuageinit /media/nuageinit $citype 2>&1 | tee -a /var/log/nuageinit.log
+ ;;
+ esac
+ if [ -n "$drive" ]; then
+ umount /media/nuageinit
+ rmdir /media/nuageinit
+ else
+ rm -rf /media/nuageinit
+ fi
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/nuageinit_post_net b/libexec/rc/rc.d/nuageinit_post_net
new file mode 100755
index 000000000000..6d2591a603af
--- /dev/null
+++ b/libexec/rc/rc.d/nuageinit_post_net
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+
+# PROVIDE: nuageinit_post_net
+# REQUIRE: NETWORKING devfs
+# BEFORE: SERVERS
+# KEYWORD: firstboot
+
+. /etc/rc.subr
+
+name="nuageinit_post_net"
+desc="Post Network Cloud Init configuration"
+start_cmd="execute_post_net"
+stop_cmd=":"
+rcvar="nuageinit_enable"
+
+execute_post_net()
+{
+ test -f /var/cache/nuageinit/user_data -o -f /var/cache/nuageinit/user-data || return
+ /usr/libexec/nuageinit /var/cache/nuageinit/ postnet | tee -a /var/log/nuageinit.log
+}
+
+# Share the same config as nuageinit
+load_rc_config nuageinit
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/nuageinit_user_data_script b/libexec/rc/rc.d/nuageinit_user_data_script
new file mode 100755
index 000000000000..decb6bf1483e
--- /dev/null
+++ b/libexec/rc/rc.d/nuageinit_user_data_script
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+
+# PROVIDE: nuageinit_user_data_script
+# REQUIRE: local
+# KEYWORD: firstboot
+
+. /etc/rc.subr
+
+name="nuageinit_user_data_script"
+desc="Execute user data script provided by cloudinit"
+start_cmd="execute_user_data_script"
+stop_cmd=":"
+rcvar="nuageinit_enable"
+
+execute_user_data_script()
+{
+ if [ -x /var/cache/nuageinit/runcmds ]; then
+ echo "Executing 'runcmd'" | tee -a /var/log/nuageinit.log
+ /var/cache/nuageinit/runcmds 2>&1 | tee -a /var/log/nuageinit.log
+ fi
+ test -x /var/cache/nuageinit/user_data || return
+ echo "Executing user_data script" | tee -a /var/log/nuageinit.log
+ /var/cache/nuageinit/user_data 2>&1 | tee -a /var/log/nuageinit.log
+}
+
+# Share the same config as nuageinit
+load_rc_config nuageinit
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/opensm b/libexec/rc/rc.d/opensm
new file mode 100755
index 000000000000..650345d81c12
--- /dev/null
+++ b/libexec/rc/rc.d/opensm
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: opensm
+# BEFORE: netif
+# REQUIRE: FILESYSTEMS
+
+. /etc/rc.subr
+
+name="opensm"
+start_cmd="opensm_start"
+rcvar="opensm_enable"
+
+: ${opensm_svcj_options:="net_basic"}
+
+command=/usr/bin/opensm
+command_args="-B"
+
+opensm_start()
+{
+ for guid in `ibstat | grep "Port GUID" | cut -d ':' -f2`; do
+ [ -z "${rc_quiet}" ] && echo "Starting ${guid} opensm."
+ ${command} ${command_args} -g ${guid} >> /dev/null
+ done
+}
+
+load_rc_config $name
+run_rc_command $*
diff --git a/libexec/rc/rc.d/os-release b/libexec/rc/rc.d/os-release
new file mode 100755
index 000000000000..0f8ee71e06b4
--- /dev/null
+++ b/libexec/rc/rc.d/os-release
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: os-release
+# REQUIRE: mountcritremote FILESYSTEMS
+# BEFORE: LOGIN
+
+. /etc/rc.subr
+
+: ${osrelease_file:=/var/run/os-release}
+: ${osrelease_perms:=444}
+name="osrelease"
+desc="Update ${osrelease_file}"
+rcvar="osrelease_enable"
+start_cmd="osrelease_start"
+stop_cmd=":"
+
+osrelease_start()
+{
+ local _version _version_id
+
+ startmsg -n "Updating ${osrelease_file} "
+ _version=$(freebsd-version -u)
+ _version_id=${_version%%[^0-9.]*}
+ t=$(mktemp -t os-release)
+ cat > "$t" <<-__EOF__
+ NAME=FreeBSD
+ VERSION="$_version"
+ VERSION_ID="$_version_id"
+ ID=freebsd
+ ANSI_COLOR="0;31"
+ PRETTY_NAME="FreeBSD $_version"
+ CPE_NAME="cpe:/o:freebsd:freebsd:$_version_id"
+ HOME_URL="https://FreeBSD.org/"
+ BUG_REPORT_URL="https://bugs.FreeBSD.org/"
+__EOF__
+ install -C -o root -g wheel -m ${osrelease_perms} "$t" "${osrelease_file}"
+ rm -f "$t"
+ startmsg 'done.'
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+osrelease_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/pf b/libexec/rc/rc.d/pf
new file mode 100755
index 000000000000..46fb085e5175
--- /dev/null
+++ b/libexec/rc/rc.d/pf
@@ -0,0 +1,94 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: pf
+# REQUIRE: FILESYSTEMS netif pflog pfsync routing
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="pf"
+desc="Packet filter"
+rcvar="pf_enable"
+load_rc_config $name
+start_cmd="pf_start"
+stop_cmd="pf_stop"
+check_cmd="pf_check"
+reload_cmd="pf_reload"
+resync_cmd="pf_resync"
+status_cmd="pf_status"
+extra_commands="check reload resync"
+required_files="$pf_rules"
+required_modules="pf"
+
+# doesn't make sense to run in a svcj: config setting
+pf_svcj="NO"
+
+pf_fallback()
+{
+ warn "Unable to load $pf_rules."
+
+ if ! checkyesno pf_fallback_rules_enable; then
+ return
+ fi
+
+ if [ -f $pf_fallback_rules_file ]; then
+ warn "Loading fallback rules file: $pf_fallback_rules_file"
+ $pf_program -f "$pf_fallback_rules_file" $pf_flags
+ else
+ warn "Loading fallback rules: $pf_fallback_rules"
+ echo "$pf_fallback_rules" | $pf_program -f - $pf_flags
+ fi
+}
+
+pf_start()
+{
+ startmsg -n 'Enabling pf'
+ $pf_program -F all > /dev/null 2>&1
+ $pf_program -f "$pf_rules" $pf_flags || pf_fallback
+ if ! $pf_program -s info | grep -q "Enabled" ; then
+ $pf_program -eq
+ fi
+ startmsg '.'
+}
+
+pf_stop()
+{
+ if $pf_program -s info | grep -q "Enabled" ; then
+ echo -n 'Disabling pf'
+ $pf_program -dq
+ echo '.'
+ fi
+}
+
+pf_check()
+{
+ echo "Checking pf rules."
+ $pf_program -n -f "$pf_rules" $pf_flags
+}
+
+pf_reload()
+{
+ echo "Reloading pf rules."
+ pf_resync
+}
+
+pf_resync()
+{
+ $pf_program -n -f "$pf_rules" $pf_flags || return 1
+ $pf_program -f "$pf_rules" $pf_flags
+}
+
+pf_status()
+{
+ if ! [ -c /dev/pf ] ; then
+ echo "pf.ko is not loaded"
+ return 1
+ else
+ $pf_program -s info
+ $pf_program -s Running >/dev/null
+ fi
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/pflog b/libexec/rc/rc.d/pflog
new file mode 100755
index 000000000000..b47252a23e0f
--- /dev/null
+++ b/libexec/rc/rc.d/pflog
@@ -0,0 +1,111 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: pflog
+# REQUIRE: FILESYSTEMS netif
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="pflog"
+desc="Packet filter logging interface"
+rcvar="pflog_enable"
+command="/sbin/pflogd"
+pidfile="/var/run/pflogd.pid"
+start_precmd="pflog_prestart"
+stop_postcmd="pflog_poststop"
+extra_commands="reload resync"
+
+# no svcj options needed
+: ${pflog_svcj_options:=""}
+
+# for backward compatibility
+resync_cmd="pflog_resync"
+
+pflog_prestart()
+{
+ load_kld pflog || return 1
+
+ # create pflog_dev interface if needed
+ if ! ifconfig $pflog_dev > /dev/null 2>&1; then
+ if ! ifconfig $pflog_dev create; then
+ warn "could not create $pflog_dev."
+ return 1
+ fi
+ fi
+
+ # set pflog_dev interface to up state
+ if ! ifconfig $pflog_dev up; then
+ warn "could not bring up $pflog_dev."
+ return 1
+ fi
+
+ # -p flag requires stripping pidfile's leading /var/run and trailing .pid
+ pidfile=$(echo $pidfile | sed -e 's|/var/run/||' -e 's|.pid$||')
+
+ # prepare the command line for pflogd
+ rc_flags="-p $pidfile -f $pflog_logfile -i $pflog_dev $rc_flags"
+
+ # report we're ready to run pflogd
+ return 0
+}
+
+pflog_poststop()
+{
+ if ! ifconfig $pflog_dev down; then
+ warn "could not bring down $pflog_dev."
+ return 1
+ fi
+
+ if [ "$pflog_instances" ] && [ -n "$pflog_instances" ]; then
+ rm $pidfile
+ fi
+
+ return 0
+}
+
+# for backward compatibility
+pflog_resync()
+{
+ run_rc_command reload
+}
+
+load_rc_config $name
+
+# precmd is not compatible with svcj
+pflog_svcj="NO"
+
+# Check if spawning multiple pflogd and told what to spawn
+if [ -n "$2" ]; then
+ # Set required variables
+ eval pflog_dev=\$pflog_${2}_dev
+ eval pflog_logfile=\$pflog_${2}_logfile
+ eval pflog_flags=\$pflog_${2}_flags
+ # Check that required vars have non-zero length, warn if not.
+ if [ -z $pflog_dev ]; then
+ warn "pflog_dev not set"
+ continue
+ fi
+ if [ -z $pflog_logfile ]; then
+ warn "pflog_logfile not set"
+ continue
+ fi
+
+ # Provide a unique pidfile name for pflogd -p <pidfile> flag
+ pidfile="/var/run/pflogd.$2.pid"
+
+ # Override service name and execute command
+ name=$pflog_dev
+ run_rc_command "$1"
+# Check if spawning multiple pflogd and not told what to spawn
+elif [ "$pflog_instances" ] && [ -n "$pflog_instances" ]; then
+ # Interate through requested instances.
+ for i in $pflog_instances; do
+ /etc/rc.d/pflog $1 $i
+ done
+else
+ # Typical case, spawn single instance only.
+ pflog_dev=${pflog_dev:-"pflog0"}
+ run_rc_command "$1"
+fi
diff --git a/libexec/rc/rc.d/pfsync b/libexec/rc/rc.d/pfsync
new file mode 100755
index 000000000000..e2ba9c17cd45
--- /dev/null
+++ b/libexec/rc/rc.d/pfsync
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: pfsync
+# REQUIRE: FILESYSTEMS netif
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="pfsync"
+desc="Packet filter state table sychronisation interface"
+rcvar="pfsync_enable"
+start_precmd="pfsync_prestart"
+start_cmd="pfsync_start"
+stop_cmd="pfsync_stop"
+required_modules="pf pfsync"
+
+pfsync_prestart()
+{
+ case "$pfsync_syncdev" in
+ '')
+ warn "pfsync_syncdev is not set."
+ return 1
+ ;;
+ esac
+ return 0
+}
+
+pfsync_start()
+{
+ local _syncpeer
+
+ echo "Enabling pfsync."
+ if [ -n "${pfsync_syncpeer}" ]; then
+ _syncpeer="syncpeer ${pfsync_syncpeer}"
+ fi
+ ifconfig pfsync0 $_syncpeer syncdev $pfsync_syncdev $pfsync_ifconfig up
+}
+
+pfsync_stop()
+{
+ echo "Disabling pfsync."
+ ifconfig pfsync0 -syncdev -syncpeer down
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+pfsync_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/power_profile b/libexec/rc/rc.d/power_profile
new file mode 100755
index 000000000000..7e187bf0a67c
--- /dev/null
+++ b/libexec/rc/rc.d/power_profile
@@ -0,0 +1,99 @@
+#!/bin/sh
+#
+# Modify the power profile based on AC line state. This script is
+# usually called from devd(8).
+#
+# Arguments: 0x00 (AC offline, economy) or 0x01 (AC online, performance)
+#
+#
+
+# PROVIDE: power_profile
+# REQUIRE: FILESYSTEMS syslogd
+# KEYWORD: nojail nostart
+
+. /etc/rc.subr
+
+name="power_profile"
+desc="Modify the power profile based on AC line state"
+stop_cmd=':'
+LOGGER="logger -t power_profile -p daemon.notice"
+
+# Set a given sysctl node to a value.
+#
+# Variables:
+# $node: sysctl node to set with the new value
+# $value: HIGH for the highest performance value, LOW for the best
+# economy value, or the value itself.
+# $highest_value: maximum value for this sysctl, when $value is "HIGH"
+# $lowest_value: minimum value for this sysctl, when $value is "LOW"
+#
+sysctl_set()
+{
+ # Check if the node exists
+ if [ -z "$(sysctl -n ${node} 2> /dev/null)" ]; then
+ return
+ fi
+
+ # Get the new value, checking for special types HIGH or LOW
+ case ${value} in
+ [Hh][Ii][Gg][Hh])
+ value=${highest_value}
+ ;;
+ [Ll][Oo][Ww])
+ value=${lowest_value}
+ ;;
+ [Nn][Oo][Nn][Ee])
+ return
+ ;;
+ *)
+ ;;
+ esac
+
+ # Set the desired value
+ if [ -n "${value}" ]; then
+ if ! sysctl ${node}=${value} > /dev/null 2>&1; then
+ warn "unable to set ${node}=${value}"
+ fi
+ fi
+}
+
+if [ $# -ne 1 ]; then
+ err 1 "Usage: $0 [0x00|0x01]"
+fi
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: privileged operations
+power_profile_svcj="NO"
+
+# Find the next state (performance or economy).
+state=$1
+case ${state} in
+0x01 | '')
+ ${LOGGER} "changed to 'performance'"
+ profile="performance"
+ ;;
+0x00)
+ ${LOGGER} "changed to 'economy'"
+ profile="economy"
+ ;;
+*)
+ echo "Usage: $0 [0x00|0x01]"
+ exit 1
+esac
+
+# Set the various sysctls based on the profile's values.
+node="hw.acpi.cpu.cx_lowest"
+highest_value="C1"
+lowest_value="Cmax"
+eval value=\$${profile}_cx_lowest
+sysctl_set
+
+node="dev.cpu.0.freq"
+highest_value="`(sysctl -n dev.cpu.0.freq_levels | \
+ awk '{ split($0, a, "[/ ]"); print a[1] }' -) 2> /dev/null`"
+lowest_value="`(sysctl -n dev.cpu.0.freq_levels | \
+ awk '{ split($0, a, "[/ ]"); print a[length(a) - 1] }' -) 2> /dev/null`"
+eval value=\$${profile}_cpu_freq
+sysctl_set
+
+exit 0
diff --git a/libexec/rc/rc.d/powerd b/libexec/rc/rc.d/powerd
new file mode 100755
index 000000000000..8ebc9cc2dc7f
--- /dev/null
+++ b/libexec/rc/rc.d/powerd
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: powerd
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="powerd"
+desc="Modify the power profile based on AC line state"
+rcvar="powerd_enable"
+command="/usr/sbin/${name}"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: privileged operations
+powerd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ppp b/libexec/rc/rc.d/ppp
new file mode 100755
index 000000000000..6f41d67f8940
--- /dev/null
+++ b/libexec/rc/rc.d/ppp
@@ -0,0 +1,138 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ppp
+# REQUIRE: netif
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="ppp"
+desc="Point to Point Protocol"
+rcvar="ppp_enable"
+command="/usr/sbin/${name}"
+start_cmd="ppp_start"
+stop_cmd="ppp_stop"
+start_postcmd="ppp_poststart"
+
+ppp_start_profile()
+{
+ local _ppp_profile _ppp_mode _ppp_nat _ppp_unit
+ local _ppp_profile_cleaned _punct _punct_c
+
+ _ppp_profile=$1
+ _ppp_profile_cleaned=$1
+ _punct=". - / +"
+ for _punct_c in $_punct; do
+ _ppp_profile_cleaned=`ltr ${_ppp_profile_cleaned} ${_punct_c} '_'`
+ done
+
+ # Check for ppp profile mode override.
+ #
+ eval _ppp_mode=\$ppp_${_ppp_profile_cleaned}_mode
+ if [ -z "$_ppp_mode" ]; then
+ _ppp_mode=$ppp_mode
+ fi
+
+ # Check for ppp profile nat override.
+ #
+ eval _ppp_nat=\$ppp_${_ppp_profile_cleaned}_nat
+ if [ -z "$_ppp_nat" ]; then
+ _ppp_nat=$ppp_nat
+ fi
+
+ # Establish ppp mode.
+ #
+ if [ "${_ppp_mode}" != "ddial" -a "${_ppp_mode}" != "direct" \
+ -a "${_ppp_mode}" != "dedicated" \
+ -a "${_ppp_mode}" != "background" ]; then
+ _ppp_mode="auto"
+ fi
+
+ rc_flags="-quiet -${_ppp_mode}"
+
+ # Switch on NAT mode?
+ #
+ case ${_ppp_nat} in
+ [Yy][Ee][Ss])
+ rc_flags="$rc_flags -nat"
+ ;;
+ esac
+
+ # Check for hard wired unit
+ eval _ppp_unit=\$ppp_${_ppp_profile_cleaned}_unit
+ if [ -n "${_ppp_unit}" ]; then
+ _ppp_unit="-unit${_ppp_unit}"
+ fi
+ rc_flags="$rc_flags $_ppp_unit"
+
+ # Run!
+ #
+ su -m $ppp_user -c "$command ${rc_flags} ${_ppp_profile}"
+}
+
+ppp_start()
+{
+ local _ppp_profile _p
+
+ _ppp_profile=$*
+ if [ -z "${_ppp_profile}" ]; then
+ _ppp_profile=$ppp_profile
+ fi
+
+ startmsg -n "Starting PPP profile:"
+
+ for _p in $_ppp_profile; do
+ startmsg -n " $_p"
+ ppp_start_profile $_p
+ done
+
+ startmsg "."
+}
+
+ppp_poststart()
+{
+ # Re-Sync ipfilter and pf so they pick up any new network interfaces
+ #
+ if [ -f /etc/rc.d/ipfilter ]; then
+ /etc/rc.d/ipfilter quietresync
+ fi
+ if [ -f /etc/rc.d/pf ]; then
+ /etc/rc.d/pf quietresync
+ fi
+}
+
+ppp_stop_profile() {
+ local _ppp_profile
+
+ _ppp_profile=$1
+
+ /bin/pkill -f "^${command}.*[[:space:]]${_ppp_profile}\$" || \
+ echo -n "(not running)"
+}
+
+ppp_stop() {
+ local _ppp_profile _p
+
+ _ppp_profile=$*
+ if [ -z "${_ppp_profile}" ]; then
+ _ppp_profile=$ppp_profile
+ fi
+
+ echo -n "Stopping PPP profile:"
+
+ for _p in $_ppp_profile; do
+ echo -n " $_p"
+ ppp_stop_profile $_p
+ done
+
+ echo "."
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+ppp_svcj="NO"
+
+run_rc_command $*
diff --git a/libexec/rc/rc.d/pppoed b/libexec/rc/rc.d/pppoed
new file mode 100755
index 000000000000..5c64862c6a49
--- /dev/null
+++ b/libexec/rc/rc.d/pppoed
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: pppoed
+# REQUIRE: NETWORKING
+# BEFORE: DAEMON
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="pppoed"
+desc="Handle incoming PPP over Ethernet connections"
+rcvar="pppoed_enable"
+start_cmd="pppoed_start"
+# XXX stop_cmd will not be straightforward
+stop_cmd=":"
+
+pppoed_start()
+{
+ local _opts
+
+ if [ -n "${pppoed_provider}" ]; then
+ pppoed_flags="${pppoed_flags} -p ${pppoed_provider}"
+ fi
+ startmsg 'Starting pppoed'
+ _opts=$-; set -f
+ /usr/libexec/pppoed ${pppoed_flags} ${pppoed_interface}
+ set +f; set -${_opts}
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+pppoed_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/pwcheck b/libexec/rc/rc.d/pwcheck
new file mode 100755
index 000000000000..db42fdd0d37e
--- /dev/null
+++ b/libexec/rc/rc.d/pwcheck
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: pwcheck
+# REQUIRE: mountcritremote syslogd
+# BEFORE: DAEMON
+
+. /etc/rc.subr
+
+name="pwcheck"
+desc="Check password file correctness"
+start_cmd="pwcheck_start"
+stop_cmd=":"
+
+pwcheck_start()
+{
+ # check the password temp/lock file
+ #
+ if [ -f /etc/ptmp ]; then
+ logger -s -p auth.err \
+ "password file may be incorrect -- /etc/ptmp exists"
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+pwcheck_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/quota b/libexec/rc/rc.d/quota
new file mode 100755
index 000000000000..9a3a3d50739c
--- /dev/null
+++ b/libexec/rc/rc.d/quota
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+#
+
+# Enable/Check the quotas (must be after ypbind if using NIS)
+
+# PROVIDE: quota
+# REQUIRE: mountcritremote ypset
+# BEFORE: DAEMON
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="quota"
+desc="Enable/check the quotas"
+rcvar="quota_enable"
+load_rc_config $name
+start_cmd="quota_start"
+stop_cmd="/usr/sbin/quotaoff ${quotaoff_flags}"
+
+# doesn't make sense to run in a svcj: config setting
+quota_svcj="NO"
+
+quota_start()
+{
+ if checkyesno check_quotas; then
+ echo -n 'Checking quotas:'
+ quotacheck ${quotacheck_flags}
+ echo ' done.'
+ fi
+
+ echo -n 'Enabling quotas:'
+ quotaon ${quotaon_flags}
+ echo ' done.'
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/random b/libexec/rc/rc.d/random
new file mode 100755
index 000000000000..c34f0d1f86b4
--- /dev/null
+++ b/libexec/rc/rc.d/random
@@ -0,0 +1,158 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: random
+# REQUIRE: FILESYSTEMS
+# BEFORE: netif
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="random"
+desc="Harvest and save entropy for random device"
+start_cmd="random_start"
+stop_cmd="random_stop"
+
+extra_commands="saveseed"
+saveseed_cmd="${name}_stop"
+
+save_dev_random()
+{
+ oumask=`umask`
+ umask 077
+ for f ; do
+ debug "saving entropy to $f"
+ dd if=/dev/random of="$f" bs=4096 count=1 status=none &&
+ ( chflags nodump "$f" 2>/dev/null || : ) &&
+ chmod 600 "$f" &&
+ fsync "$f" "$(dirname "$f")"
+ done
+ umask ${oumask}
+}
+
+feed_dev_random()
+{
+ for f ; do
+ if [ -f "$f" -a -r "$f" -a -s "$f" ] ; then
+ if dd if="$f" of=/dev/random bs=4096 2>/dev/null ; then
+ debug "entropy read from $f"
+ rm -f "$f"
+ fi
+ fi
+ done
+}
+
+random_start()
+{
+
+ if [ -n "${harvest_mask}" ]; then
+ echo -n 'Setting up harvesting: '
+ ${SYSCTL} kern.random.harvest.mask=${harvest_mask} > /dev/null
+ ${SYSCTL_N} kern.random.harvest.mask_symbolic
+ fi
+
+ echo -n 'Feeding entropy: '
+
+ if [ ! -w /dev/random ] ; then
+ warn "/dev/random is not writeable"
+ return 1
+ fi
+
+ # Reseed /dev/random with previously stored entropy.
+ case ${entropy_dir:=/var/db/entropy} in
+ [Nn][Oo])
+ ;;
+ *)
+ if [ -d "${entropy_dir}" ] ; then
+ feed_dev_random "${entropy_dir}"/*
+ fi
+ ;;
+ esac
+
+ case ${entropy_file:=/entropy} in
+ [Nn][Oo])
+ ;;
+ *)
+ feed_dev_random "${entropy_file}" /var/db/entropy-file
+ save_dev_random "${entropy_file}"
+ ;;
+ esac
+
+ case ${entropy_boot_file:=/boot/entropy} in
+ [Nn][Oo])
+ ;;
+ *)
+ save_dev_random "${entropy_boot_file}"
+ ;;
+ esac
+
+ echo '.'
+}
+
+random_stop()
+{
+ # Write some entropy so when the machine reboots /dev/random
+ # can be reseeded
+ #
+ case ${entropy_file:=/entropy} in
+ [Nn][Oo])
+ ;;
+ *)
+ echo -n 'Writing entropy file: '
+ rm -f ${entropy_file} 2> /dev/null
+ oumask=`umask`
+ umask 077
+ if touch ${entropy_file} 2> /dev/null; then
+ entropy_file_confirmed="${entropy_file}"
+ else
+ # Try this as a reasonable alternative for read-only
+ # roots, diskless workstations, etc.
+ rm -f /var/db/entropy-file 2> /dev/null
+ if touch /var/db/entropy-file 2> /dev/null; then
+ entropy_file_confirmed=/var/db/entropy-file
+ fi
+ fi
+ case ${entropy_file_confirmed} in
+ '')
+ warn 'write failed (read-only fs?)'
+ ;;
+ *)
+ save_dev_random "${entropy_file_confirmed}"
+ echo '.'
+ ;;
+ esac
+ umask ${oumask}
+ ;;
+ esac
+ case ${entropy_boot_file:=/boot/entropy} in
+ [Nn][Oo])
+ ;;
+ *)
+ echo -n 'Writing early boot entropy file: '
+ rm -f ${entropy_boot_file} 2> /dev/null
+ oumask=`umask`
+ umask 077
+ if touch ${entropy_boot_file} 2> /dev/null; then
+ entropy_boot_file_confirmed="${entropy_boot_file}"
+ fi
+ case ${entropy_boot_file_confirmed} in
+ '')
+ warn 'write failed (read-only fs?)'
+ ;;
+ *)
+ save_dev_random "${entropy_boot_file_confirmed}"
+ echo '.'
+ ;;
+ esac
+ umask ${oumask}
+ ;;
+ esac
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+random_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/rarpd b/libexec/rc/rc.d/rarpd
new file mode 100755
index 000000000000..2618565ae0d1
--- /dev/null
+++ b/libexec/rc/rc.d/rarpd
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: rarpd
+# REQUIRE: DAEMON FILESYSTEMS
+# BEFORE: LOGIN
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="rarpd"
+desc="Reverse ARP daemon"
+rcvar="rarpd_enable"
+command="/usr/sbin/${name}"
+required_files="/etc/ethers"
+
+: ${rarpd_svcj_options:="net_basic"}
+
+load_rc_config $name
+pidfile="${rarpd_pidfile:-/var/run/${name}.pid}"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/rctl b/libexec/rc/rc.d/rctl
new file mode 100755
index 000000000000..96c148e78bcd
--- /dev/null
+++ b/libexec/rc/rc.d/rctl
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: rctl
+# REQUIRE: FILESYSTEMS
+# BEFORE: LOGIN
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="rctl"
+desc="Manage resource limits"
+rcvar="rctl_enable"
+start_cmd="rctl_start"
+stop_cmd="rctl_stop"
+
+rctl_start()
+{
+ if [ -f ${rctl_rules} ]; then
+ while read var comments
+ do
+ case ${var} in
+ \#*|'')
+ ;;
+ *)
+ echo "${var}"
+ ;;
+ esac
+ done < ${rctl_rules} | xargs rctl -a
+ fi
+}
+
+rctl_stop()
+{
+
+ rctl -r :
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+rctl_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/resolv b/libexec/rc/rc.d/resolv
new file mode 100755
index 000000000000..a46c7ba314e9
--- /dev/null
+++ b/libexec/rc/rc.d/resolv
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 1999 Matt Dillon
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: resolv
+# REQUIRE: netif FILESYSTEMS
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="resolv"
+rcvar="resolv_enable"
+desc="Create /etc/resolv.conf from kenv"
+start_cmd="${name}_start"
+stop_cmd=':'
+
+# if the info is available via dhcp/kenv
+# build the resolv.conf
+#
+resolv_start()
+{
+ if [ -n "`/bin/kenv dhcp.domain-name-servers 2> /dev/null`" ]; then
+ interface="`/bin/kenv boot.netif.name`"
+ (
+ if [ -n "`/bin/kenv dhcp.domain-name 2> /dev/null`" ]; then
+ echo domain `/bin/kenv dhcp.domain-name`
+ fi
+
+ set -- `/bin/kenv dhcp.domain-name-servers`
+ for ns in `IFS=','; echo $*`; do
+ echo nameserver $ns
+ done
+ ) | /sbin/resolvconf -a ${interface}:dhcp4
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+resolv_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/rfcomm_pppd_server b/libexec/rc/rc.d/rfcomm_pppd_server
new file mode 100755
index 000000000000..810c1adc8e91
--- /dev/null
+++ b/libexec/rc/rc.d/rfcomm_pppd_server
@@ -0,0 +1,126 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: rfcomm_pppd_server
+# REQUIRE: DAEMON sdpd
+# BEFORE: LOGIN
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="rfcomm_pppd_server"
+desc="RFCOMM PPP daemon"
+rcvar="rfcomm_pppd_server_enable"
+command="/usr/sbin/rfcomm_pppd"
+start_cmd="rfcomm_pppd_server_start"
+stop_cmd="rfcomm_pppd_server_stop"
+required_modules="ng_btsocket"
+
+rfcomm_pppd_server_start_profile()
+{
+ local _profile _profile_cleaned _punct _punct_c
+ local _bdaddr _channel _x
+
+ _profile=$1
+ _profile_cleaned=$1
+
+ _punct=". - / +"
+ for _punct_c in ${_punct} ; do
+ _profile_cleaned=`ltr ${_profile_cleaned} ${_punct_c} '_'`
+ done
+
+ rc_flags=""
+
+ # Check for RFCOMM PPP profile bdaddr override
+ #
+ eval _bdaddr=\$rfcomm_pppd_server_${_profile_cleaned}_bdaddr
+ if [ -n "${_bdaddr}" ]; then
+ rc_flags="${rc_flags} -a ${_bdaddr}"
+ fi
+
+ # Check for RFCOMM PPP profile channel override
+ #
+ eval _channel=\$rfcomm_pppd_server_${_profile_cleaned}_channel
+ if [ -z "${_channel}" ]; then
+ _channel=1
+ fi
+ rc_flags="${rc_flags} -C ${_channel}"
+
+ # Check for RFCOMM PPP profile register SP override
+ #
+ eval _x=\$rfcomm_pppd_server_${_profile_cleaned}_register_sp
+ if [ -n "${_x}" ]; then
+ if checkyesno "rfcomm_pppd_server_${_profile_cleaned}_register_sp" ; then
+ rc_flags="${rc_flags} -S"
+ fi
+ fi
+
+ # Check for RFCOMM PPP profile register DUN override
+ #
+ eval _x=\$rfcomm_pppd_server_${_profile_cleaned}_register_dun
+ if [ -n "${_x}" ]; then
+ if checkyesno "rfcomm_pppd_server_${_profile_cleaned}_register_dun" ; then
+ rc_flags="${rc_flags} -D"
+ fi
+ fi
+
+ # Run!
+ #
+ $command -s ${rc_flags} -l ${_profile}
+}
+
+rfcomm_pppd_server_stop_profile()
+{
+ local _profile
+
+ _profile=$1
+
+ /bin/pkill -f "^${command}.*[[:space:]]${_profile}\$" || \
+ echo -n "(not running)"
+}
+
+rfcomm_pppd_server_start()
+{
+ local _profile _p
+
+ _profile=$*
+ if [ -z "${_profile}" ]; then
+ _profile=${rfcomm_pppd_server_profile}
+ fi
+
+ startmsg -n "Starting RFCOMM PPP profile:"
+
+ for _p in ${_profile} ; do
+ startmsg -n " ${_p}"
+ rfcomm_pppd_server_start_profile ${_p}
+ done
+
+ startmsg "."
+}
+
+rfcomm_pppd_server_stop()
+{
+ local _profile _p
+
+ _profile=$*
+ if [ -z "${_profile}" ]; then
+ _profile=${rfcomm_pppd_server_profile}
+ fi
+
+ echo -n "Stopping RFCOMM PPP profile:"
+
+ for _p in ${_profile} ; do
+ echo -n " ${_p}"
+ rfcomm_pppd_server_stop_profile ${_p}
+ done
+
+ echo "."
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+rfcomm_pppd_server_svcj="NO"
+
+run_rc_command $*
diff --git a/libexec/rc/rc.d/root b/libexec/rc/rc.d/root
new file mode 100755
index 000000000000..e1dad6270e7d
--- /dev/null
+++ b/libexec/rc/rc.d/root
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: root
+# REQUIRE: fsck
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="root"
+desc="Mount root filesystem read/write"
+start_cmd="root_start"
+stop_cmd=":"
+
+root_start()
+{
+ # root normally must be read/write, but if this is a BOOTP NFS
+ # diskless boot it does not have to be.
+ #
+ case ${root_rw_mount} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ if ! mount -uw /; then
+ echo 'Mounting root filesystem rw failed, startup aborted'
+ stop_boot true
+ fi
+ ;;
+ esac
+
+ umount -a >/dev/null 2>&1
+
+ # If we booted a special kernel remove the record
+ # so we will boot the default kernel next time.
+ if [ -x /sbin/nextboot ]; then
+ /sbin/nextboot -D > /dev/null 2>&1
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: mounting / config setting
+root_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/route6d b/libexec/rc/rc.d/route6d
new file mode 100755
index 000000000000..873efdeb123c
--- /dev/null
+++ b/libexec/rc/rc.d/route6d
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: route6d
+# REQUIRE: netif routing
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="route6d"
+desc="RIP6 routing daemon"
+rcvar="route6d_enable"
+
+: ${route6d_svcj_options:="net_basic"}
+
+set_rcvar_obsolete ipv6_router_enable route6d_enable
+set_rcvar_obsolete ipv6_router route6d_program
+set_rcvar_obsolete ipv6_router_flags route6d_flags
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/routed b/libexec/rc/rc.d/routed
new file mode 100755
index 000000000000..9338cf034edd
--- /dev/null
+++ b/libexec/rc/rc.d/routed
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: routed dynamicrouting
+# REQUIRE: netif routing
+# BEFORE: NETWORKING
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+
+name="routed"
+desc="Network RIP and router discovery routing daemon"
+rcvar="routed_enable"
+
+: ${routed_svcj_options:="net_basic"}
+
+set_rcvar_obsolete router_enable routed_enable
+set_rcvar_obsolete router routed_program
+set_rcvar_obsolete router_flags routed_flags
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/routing b/libexec/rc/rc.d/routing
new file mode 100755
index 000000000000..dd75604125a3
--- /dev/null
+++ b/libexec/rc/rc.d/routing
@@ -0,0 +1,442 @@
+#!/bin/sh
+#
+# Configure routing and miscellaneous network tunables
+#
+#
+
+# PROVIDE: routing
+# REQUIRE: netif ppp stf
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="routing"
+desc="Routing setup"
+start_cmd="routing_start doall"
+stop_cmd="routing_stop"
+extra_commands="options static"
+static_cmd="routing_start static"
+options_cmd="routing_start options"
+
+ROUTE_CMD="/sbin/route"
+
+routing_start()
+{
+ local _cmd _af _if _a _ret
+ _cmd=$1
+ _af=$2
+ _if=$3
+ _ret=0
+
+ case $_if in
+ ""|[Aa][Ll][Ll]|[Aa][Nn][Yy]) _if="" ;;
+ esac
+
+ case $_af in
+ ""|[Aa][Ll][Ll]|[Aa][Nn][Yy])
+ for _a in inet inet6; do
+ afexists $_a || continue
+ setroutes $_cmd $_a $_if || _ret=1
+ done
+ ;;
+ *)
+ if afexists $_af; then
+ setroutes $_cmd $_af $_if || _ret=1
+ else
+ err 1 "Unsupported address family: $_af."
+ fi
+ ;;
+ esac
+
+ return $_ret
+}
+
+routing_stop()
+{
+ local _af _if _a
+ _af=$1
+ _if=$2
+
+ case $_if in
+ ""|[Aa][Ll][Ll]|[Aa][Nn][Yy]) _if="" ;;
+ esac
+
+ case $_af in
+ ""|[Aa][Ll][Ll]|[Aa][Nn][Yy])
+ for _a in inet inet6; do
+ afexists $_a || continue
+ eval static_${_a} delete $_if
+ # When $_if is specified, do not flush routes.
+ if ! [ -n "$_if" ]; then
+ eval routing_stop_${_a}
+ fi
+ done
+ ;;
+ *)
+ if afexists $_af; then
+ eval static_${_af} delete $_if
+ # When $_if is specified, do not flush routes.
+ if ! [ -n "$_if" ]; then
+ eval routing_stop_${_af}
+ fi
+ else
+ err 1 "Unsupported address family: $_af."
+ fi
+ ;;
+ esac
+}
+
+setroutes()
+{
+ local _ret
+ _ret=0
+ case $1 in
+ static)
+ static_$2 add $3
+ _ret=$?
+ ;;
+ options)
+ options_$2
+ ;;
+ doall)
+ static_$2 add $3
+ _ret=$?
+ options_$2
+ ;;
+ esac
+ return $_ret
+}
+
+routing_stop_inet()
+{
+ ${ROUTE_CMD} -n flush -inet
+}
+
+routing_stop_inet6()
+{
+ local i
+
+ ${ROUTE_CMD} -n flush -inet6
+ for i in `list_net_interfaces`; do
+ if ipv6if $i; then
+ ifconfig $i inet6 -defaultif
+ fi
+ done
+}
+
+get_fibmod()
+{
+ local _fibs
+
+ _fibs=$((`${SYSCTL_N} net.fibs` - 1))
+ if [ ${_fibs} -gt 0 ]; then
+ echo "-fib 0-${_fibs}"
+ else
+ echo
+ fi
+}
+
+static_inet()
+{
+ local _action _if _skip _fibmod _fibs
+ _action=$1
+ _if=$2
+
+ _fibmod=`get_fibmod`
+ _fibs=$((`${SYSCTL_N} net.fibs` - 1))
+
+ # Provide loopback route in all routing tables. This has to come
+ # first so that any following routes can be added.
+ static_routes="_loopback ${static_routes}"
+ route__loopback="-inet 127.0.0.1 -iface lo0 ${_fibmod}"
+
+ # Add default route.
+ case ${defaultrouter} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ static_routes="${static_routes} _default"
+ route__default="default ${defaultrouter}"
+ ;;
+ esac
+
+ # Add default routes for fibs
+ if [ ${_fibs} -gt 0 ]; then
+ for _fibnum in `jot ${_fibs}` ; do
+ eval _fib_gw=\${defaultrouter_fib${_fibnum}}
+ case ${_fib_gw} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ static_routes="${static_routes} _default_fib${_fibnum}"
+ eval route__default_fib${_fibnum}="'default ${_fib_gw} -fib ${_fibnum}'"
+ ;;
+ esac
+ done
+ fi
+
+
+ # Install configured routes.
+ if [ -n "${static_routes}" ]; then
+ for i in ${static_routes}; do
+ _skip=0
+ if [ -n "$_if" ]; then
+ case $i in
+ *:$_if) ;;
+ *) _skip=1 ;;
+ esac
+ fi
+ if [ $_skip = 0 ]; then
+ route_args=`get_if_var ${i%:*} route_IF`
+ if [ -n "$route_args" ]; then
+ ${ROUTE_CMD} ${_action} ${route_args}
+ else
+ warn "route_${i%:*} not found."
+ fi
+ fi
+ done
+ fi
+}
+
+static_inet6()
+{
+ local _action _if _skip fibmod _fibs
+ _action=$1
+ _if=$2
+
+ fibmod=`get_fibmod`
+ _fibs=$((`${SYSCTL_N} net.fibs` - 1))
+
+ # Add pre-defined static routes first.
+ ipv6_static_routes="_v4mapped _v4compat ${ipv6_static_routes}"
+ ipv6_static_routes="_lla _llma ${ipv6_static_routes}"
+ ipv6_static_routes="_loopback ${ipv6_static_routes}"
+
+ # disallow "internal" addresses to appear on the wire
+ ipv6_route__v4mapped="::ffff:0.0.0.0 -prefixlen 96 ::1 -reject ${fibmod}"
+ ipv6_route__v4compat="::0.0.0.0 -prefixlen 96 ::1 -reject ${fibmod}"
+
+ # Create a loopback route in every fib
+ ipv6_route__loopback="::1 -prefixlen 128 -iface lo0 ${fibmod}"
+
+ # Disallow link-local unicast packets without outgoing scope
+ # identifiers. However, if you set "ipv6_default_interface",
+ # for the host case, you will allow to omit the identifiers.
+ # Under this configuration, the packets will go to the default
+ # interface.
+ ipv6_route__lla="fe80:: -prefixlen 10 ::1 -reject ${fibmod}"
+ ipv6_route__llma="ff02:: -prefixlen 16 ::1 -reject ${fibmod}"
+
+ # Add default route.
+ case ${ipv6_defaultrouter} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ ipv6_static_routes="${ipv6_static_routes} _default"
+ ipv6_route__default="default ${ipv6_defaultrouter}"
+ ;;
+ esac
+
+ # Add default routes for fibs
+ if [ ${_fibs} -gt 0 ]; then
+ for _fibnum in `jot ${_fibs}` ; do
+ eval _fib_gw=\${ipv6_defaultrouter_fib${_fibnum}}
+ case ${_fib_gw} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ ipv6_static_routes="${ipv6_static_routes} _default_fib${_fibnum}"
+ eval ipv6_route__default_fib${_fibnum}="'default ${_fib_gw} -fib ${_fibnum}'"
+ ;;
+ esac
+ done
+ fi
+
+
+ # Install configured routes.
+ if [ -n "${ipv6_static_routes}" ]; then
+ for i in ${ipv6_static_routes}; do
+ _skip=0
+ if [ -n "$_if" ]; then
+ case $i in
+ *:$_if) ;;
+ *) _skip=1 ;;
+ esac
+ fi
+ if [ $_skip = 0 ]; then
+ ipv6_route_args=`get_if_var ${i%:*} ipv6_route_IF`
+ if [ -n "$ipv6_route_args" ]; then
+ ${ROUTE_CMD} ${_action} \
+ -inet6 ${ipv6_route_args}
+ else
+ warn "route_${i%:*} not found"
+ fi
+ fi
+ done
+ fi
+
+ # Install the "default interface" to kernel, which will be used
+ # as the default route when there's no router.
+
+ # Disable installing the default interface when we act
+ # as router to avoid conflict between the default
+ # router list and the manual configured default route.
+ if checkyesno ipv6_gateway_enable; then
+ return
+ fi
+
+ case "${ipv6_default_interface}" in
+ [Nn][Oo] | [Nn][Oo][Nn][Ee])
+ return
+ ;;
+ [Aa][Uu][Tt][Oo] | "")
+ for i in ${ipv6_network_interfaces}; do
+ case $i in
+ [Nn][Oo][Nn][Ee])
+ return
+ ;;
+ lo0)
+ continue
+ ;;
+ esac
+ laddr=`network6_getladdr $i exclude_tentative`
+ case ${laddr} in
+ '')
+ ;;
+ *)
+ ipv6_default_interface=$i
+ break
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ ifconfig ${ipv6_default_interface} inet6 defaultif
+ ${SYSCTL} net.inet6.ip6.use_defaultzone=1 > /dev/null
+}
+
+ropts_init()
+{
+ if [ -z "${_ropts_initdone}" ]; then
+ echo -n "Additional $1 routing options:"
+ _ropts_initdone=yes
+ fi
+}
+
+_check_dynamicrouting()
+{
+ local skip file name rcvar
+
+ # copied from /etc/rc
+ skip="-s nostart"
+ if check_jail jailed; then
+ skip="$skip -s nojail"
+ fi
+ [ -n "$local_startup" ] && find_local_scripts_new
+ [ -n "$system_rc" ] && find_system_scripts
+
+ for file in $( rcorder ${skip} ${system_rc} ${local_rc} 2>/dev/null |
+ xargs grep -lE '^# PROVIDE:.*\<dynamicrouting\>' ); do
+ (set -- enabled; . $file) && return 0;
+ done
+
+ return 1
+}
+
+options_inet()
+{
+ local _icmp_drop_redirect
+
+ _ropts_initdone=
+ if checkyesno icmp_bmcastecho; then
+ ropts_init inet
+ echo -n ' broadcast ping responses=YES'
+ ${SYSCTL} net.inet.icmp.bmcastecho=1 > /dev/null
+ else
+ ${SYSCTL} net.inet.icmp.bmcastecho=0 > /dev/null
+ fi
+
+ _icmp_drop_redirect="${icmp_drop_redirect}"
+ case "${_icmp_drop_redirect}" in
+ [Aa][Uu][Tt][Oo] | "")
+ if _check_dynamicrouting; then
+ _icmp_drop_redirect="yes"
+ else
+ _icmp_drop_redirect="no"
+ fi
+ ;;
+ esac
+ if checkyesno _icmp_drop_redirect; then
+ ropts_init inet
+ echo -n ' ignore ICMP redirect=YES'
+ ${SYSCTL} net.inet.icmp.drop_redirect=1 > /dev/null
+ else
+ ${SYSCTL} net.inet.icmp.drop_redirect=0 > /dev/null
+ fi
+
+ if checkyesno icmp_log_redirect; then
+ ropts_init inet
+ echo -n ' log ICMP redirect=YES'
+ ${SYSCTL} net.inet.icmp.log_redirect=1 > /dev/null
+ else
+ ${SYSCTL} net.inet.icmp.log_redirect=0 > /dev/null
+ fi
+
+ if checkyesno gateway_enable; then
+ ropts_init inet
+ echo -n ' gateway=YES'
+ ${SYSCTL} net.inet.ip.forwarding=1 > /dev/null
+ else
+ ${SYSCTL} net.inet.ip.forwarding=0 > /dev/null
+ fi
+
+ if checkyesno forward_sourceroute; then
+ ropts_init inet
+ echo -n ' do source routing=YES'
+ ${SYSCTL} net.inet.ip.sourceroute=1 > /dev/null
+ else
+ ${SYSCTL} net.inet.ip.sourceroute=0 > /dev/null
+ fi
+
+ if checkyesno accept_sourceroute; then
+ ropts_init inet
+ echo -n ' accept source routing=YES'
+ ${SYSCTL} net.inet.ip.accept_sourceroute=1 > /dev/null
+ else
+ ${SYSCTL} net.inet.ip.accept_sourceroute=0 > /dev/null
+ fi
+
+ if checkyesno arpproxy_all; then
+ ropts_init inet
+ echo -n ' ARP proxyall=YES'
+ ${SYSCTL} net.link.ether.inet.proxyall=1 > /dev/null
+ else
+ ${SYSCTL} net.link.ether.inet.proxyall=0 > /dev/null
+ fi
+
+ [ -n "${_ropts_initdone}" ] && echo '.'
+}
+
+options_inet6()
+{
+ _ropts_initdone=
+
+ if checkyesno ipv6_gateway_enable; then
+ ropts_init inet6
+ echo -n ' gateway=YES'
+ ${SYSCTL} net.inet6.ip6.forwarding=1 > /dev/null
+ else
+ ${SYSCTL} net.inet6.ip6.forwarding=0 > /dev/null
+ fi
+
+ [ -n "${_ropts_initdone}" ] && echo '.'
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+routing_svcj="NO"
+
+run_rc_command "$@"
diff --git a/libexec/rc/rc.d/rpcbind b/libexec/rc/rc.d/rpcbind
new file mode 100755
index 000000000000..c393df666219
--- /dev/null
+++ b/libexec/rc/rc.d/rpcbind
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: rpcbind
+# REQUIRE: NETWORKING ntpdate syslogd
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="rpcbind"
+desc="Universal addresses to RPC program number mapper"
+rcvar="rpcbind_enable"
+command="/usr/sbin/${name}"
+
+: ${rpcbind_svcj_options:="net_basic"}
+
+stop_postcmd='/bin/rm -f /var/run/rpcbind.*'
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/rtadvd b/libexec/rc/rc.d/rtadvd
new file mode 100755
index 000000000000..99fec22604aa
--- /dev/null
+++ b/libexec/rc/rc.d/rtadvd
@@ -0,0 +1,77 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: rtadvd
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: nojailvnet shutdown
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="rtadvd"
+desc="Router advertisement daemon"
+rcvar="rtadvd_enable"
+command="/usr/sbin/${name}"
+extra_commands="reload"
+reload_cmd="rtadvd_reload"
+start_precmd="rtadvd_precmd"
+
+: ${rtadvd_svcj_options:="net_basic"}
+
+rtadvd_precmd()
+{
+ # This should be enabled with a great care.
+ # You may want to fine-tune /etc/rtadvd.conf.
+ #
+ # And if you wish your rtadvd to receive and process
+ # router renumbering messages, specify your Router Renumbering
+ # security policy by -R option.
+ #
+ # See `man 3 ipsec_set_policy` for IPsec policy specification
+ # details.
+ # (CAUTION: This enables your routers prefix renumbering
+ # from another machine, so if you enable this, do it with
+ # enough care.)
+ #
+ # If specific interfaces haven't been specified,
+ # get a list of interfaces and enable it on them
+ #
+ case ${rtadvd_interfaces} in
+ [Aa][Uu][Tt][Oo]|'')
+ command_args=
+ for i in `list_net_interfaces`; do
+ case $i in
+ lo0) continue ;;
+ esac
+ if ipv6if $i; then
+ command_args="${command_args} ${i}"
+ fi
+ done
+ ;;
+ [Nn][Oo][Nn][Ee])
+ ;;
+ *)
+ command_args="${rtadvd_interfaces}"
+ ;;
+ esac
+
+ # Enable Router Renumbering, unicast case
+ # (use correct src/dst addr)
+ # rtadvd -R "in ipsec ah/transport/fec0:0:0:1::1-fec0:0:0:10::1/require" ${ipv6_network_interfaces}
+ # Enable Router Renumbering, multicast case
+ # (use correct src addr)
+ # rtadvd -R "in ipsec ah/transport/ff05::2-fec0:0:0:10::1/require" ${ipv6_network_interfaces}
+ return 0
+}
+
+rtadvd_reload() {
+ /usr/sbin/rtadvctl reload
+}
+
+load_rc_config $name
+
+# precmd is not compatible with svcj
+rtadvd_svcj="NO"
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/rtsold b/libexec/rc/rc.d/rtsold
new file mode 100755
index 000000000000..5578af5a367f
--- /dev/null
+++ b/libexec/rc/rc.d/rtsold
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: rtsold
+# REQUIRE: netif
+# BEFORE: NETWORKING
+# KEYWORD: nojailvnet shutdown
+
+. /etc/rc.subr
+
+name="rtsold"
+desc="Router solicitation daemon"
+rcvar="rtsold_enable"
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+start_postcmd="rtsold_poststart"
+
+: ${rtsold_svcj_options:="net_basic"}
+
+rtsold_poststart()
+{
+ # wait for DAD
+ sleep $(($(${SYSCTL_N} net.inet6.ip6.dad_count) + 1))
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/rwho b/libexec/rc/rc.d/rwho
new file mode 100755
index 000000000000..f35bcda30ebf
--- /dev/null
+++ b/libexec/rc/rc.d/rwho
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: rwho
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="rwhod"
+desc="System status server"
+rcvar="rwhod_enable"
+command="/usr/sbin/${name}"
+
+: ${rwhod_svcj_options:="net_basic"}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/savecore b/libexec/rc/rc.d/savecore
new file mode 100755
index 000000000000..889476591dac
--- /dev/null
+++ b/libexec/rc/rc.d/savecore
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: savecore
+# REQUIRE: dumpon ddb syslogd
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="savecore"
+rcvar="savecore_enable"
+desc="Save a core dump of the operating system"
+start_cmd="savecore_start"
+start_precmd="savecore_prestart"
+stop_cmd=":"
+
+savecore_prestart()
+{
+ # Quit if we have no dump device
+ case ${dumpdev} in
+ [Nn][Oo])
+ debug 'No dump device. Quitting.'
+ return 1
+ ;;
+ [Aa][Uu][Tt][Oo] | '')
+ if [ ! -L /dev/dumpdev ]; then
+ return 1
+ fi
+ dumpdev=`/bin/realpath /dev/dumpdev`
+ ;;
+ esac
+
+ # If there is no crash directory set it now
+ case ${dumpdir} in
+ '')
+ dumpdir='/var/crash'
+ ;;
+ [Nn][Oo])
+ dumpdir='NO'
+ ;;
+ esac
+
+ if [ ! -c "${dumpdev}" ]; then
+ warn "Dump device does not exist. Savecore not run."
+ return 1
+ fi
+
+ if [ ! -d "${dumpdir}" ]; then
+ warn "Dump directory does not exist. Savecore not run."
+ return 1
+ fi
+ return 0
+}
+
+savecore_start()
+{
+ local dev
+
+ case "${dumpdev}" in
+ [Aa][Uu][Tt][Oo])
+ dev=
+ ;;
+ *)
+ dev="${dumpdev}"
+ ;;
+ esac
+
+ if savecore -C "${dev}" >/dev/null; then
+ savecore ${savecore_flags} ${dumpdir} ${dumpdev}
+ if checkyesno crashinfo_enable; then
+ ${crashinfo_program} -b -d ${dumpdir}
+ fi
+ sync
+ else
+ startmsg 'No core dumps found.'
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+savecore_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/sdpd b/libexec/rc/rc.d/sdpd
new file mode 100755
index 000000000000..a7bf51ecdc75
--- /dev/null
+++ b/libexec/rc/rc.d/sdpd
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: sdpd
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="sdpd"
+desc="Bluetooth Service Discovery Protocol daemon "
+command="/usr/sbin/${name}"
+rcvar="sdpd_enable"
+required_modules="ng_btsocket"
+
+load_rc_config $name
+control="${sdpd_control:-/var/run/sdp}"
+group="${sdpd_groupname:-nobody}"
+user="${sdpd_username:-nobody}"
+command_args="-c ${control} -g ${group} -u ${user}"
+
+# doesn't make sense to run in a svcj: nojail keyword
+sdpd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/securelevel b/libexec/rc/rc.d/securelevel
new file mode 100755
index 000000000000..e5c5a410cf62
--- /dev/null
+++ b/libexec/rc/rc.d/securelevel
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: securelevel
+# REQUIRE: adjkerntz ipfw pf sysctl_lastload
+
+. /etc/rc.subr
+
+name="securelevel"
+desc="Securelevel configuration"
+rcvar='kern_securelevel_enable'
+start_cmd="securelevel_start"
+stop_cmd=":"
+
+securelevel_start()
+{
+ if [ ${kern_securelevel} -ge 0 ]; then
+ echo 'Raising kernel security level: '
+ ${SYSCTL} kern.securelevel=${kern_securelevel}
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+securelevel_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/sendmail b/libexec/rc/rc.d/sendmail
new file mode 100755
index 000000000000..a9d37f3f7d69
--- /dev/null
+++ b/libexec/rc/rc.d/sendmail
@@ -0,0 +1,229 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: mail
+# REQUIRE: LOGIN FILESYSTEMS
+# KEYWORD: shutdown
+#
+# We make mail start late, so that things like .forward's are not processed
+# until the system is fully operational.
+
+# XXX - Get together with sendmail mantainer to figure out how to
+# better handle SENDMAIL_ENABLE and 3rd party MTAs.
+#
+. /etc/rc.subr
+
+name="sendmail"
+desc="Electronic mail transport agent"
+rcvar="sendmail_enable"
+required_files="/etc/mail/${name}.cf"
+start_precmd="sendmail_precmd"
+
+: ${sendmail_svcj_options:="net_basic"}
+
+load_rc_config $name
+command=${sendmail_program:-/usr/sbin/${name}}
+pidfile=${sendmail_pidfile:-/var/run/${name}.pid}
+procname=${sendmail_procname:-/usr/sbin/${name}}
+
+CERTDIR=/etc/mail/certs
+
+case ${sendmail_enable} in
+[Nn][Oo][Nn][Ee])
+ sendmail_enable="NO"
+ sendmail_submit_enable="NO"
+ sendmail_outbound_enable="NO"
+ sendmail_msp_queue_enable="NO"
+ ;;
+esac
+
+# If sendmail_enable=yes, don't need submit or outbound daemon
+if checkyesno sendmail_enable; then
+ sendmail_submit_enable="NO"
+ sendmail_outbound_enable="NO"
+ _sendmail_run=true
+fi
+
+# If sendmail_submit_enable=yes, don't need outbound daemon
+if checkyesno sendmail_submit_enable; then
+ name="sendmail_submit"
+ rcvar="sendmail_submit_enable"
+ sendmail_outbound_enable="NO"
+ _sendmail_run=true
+fi
+
+if checkyesno sendmail_outbound_enable; then
+ name="sendmail_outbound"
+ rcvar="sendmail_outbound_enable"
+ _sendmail_run=true
+fi
+
+if checkyesno sendmail_msp_queue_enable; then
+ _sendmail_msp_queue_run=true
+else
+ # Make sure run_rc_command is called at least once.
+ _sendmail_run=true
+fi
+
+sendmail_cert_create()
+{
+ cnname="${sendmail_cert_cn:-`hostname`}"
+ cnname="${cnname:-amnesiac}"
+
+ # based upon:
+ # http://www.sendmail.org/~ca/email/other/cagreg.html
+ CAdir=`mktemp -d` &&
+ certpass=`(date; ps ax ; hostname) | md5 -q`
+
+ # make certificate authority
+ ( cd "$CAdir" &&
+ chmod 700 "$CAdir" &&
+ mkdir certs crl newcerts &&
+ echo "01" > serial &&
+ :> index.txt &&
+
+ cat <<-OPENSSL_CNF > openssl.cnf &&
+ RANDFILE = $CAdir/.rnd
+ [ ca ]
+ default_ca = CA_default
+ [ CA_default ]
+ dir = .
+ certs = \$dir/certs # Where the issued certs are kept
+ crl_dir = \$dir/crl # Where the issued crl are kept
+ database = \$dir/index.txt # database index file.
+ new_certs_dir = \$dir/newcerts # default place for new certs.
+ certificate = \$dir/cacert.pem # The CA certificate
+ serial = \$dir/serial # The current serial number
+ crlnumber = \$dir/crlnumber # the current crl number
+ crl = \$dir/crl.pem # The current CRL
+ private_key = \$dir/cakey.pem
+ x509_extensions = usr_cert # The extensions to add to the cert
+ name_opt = ca_default # Subject Name options
+ cert_opt = ca_default # Certificate field options
+ default_days = 365 # how long to certify for
+ default_crl_days= 30 # how long before next CRL
+ default_md = default # use public key default MD
+ preserve = no # keep passed DN ordering
+ policy = policy_anything
+ [ policy_anything ]
+ countryName = optional
+ stateOrProvinceName = optional
+ localityName = optional
+ organizationName = optional
+ organizationalUnitName = optional
+ commonName = supplied
+ emailAddress = optional
+ [ req ]
+ default_bits = 2048
+ default_keyfile = privkey.pem
+ distinguished_name = req_distinguished_name
+ attributes = req_attributes
+ x509_extensions = v3_ca # The extensions to add to the self signed cert
+ string_mask = utf8only
+ prompt = no
+ [ req_distinguished_name ]
+ countryName = XX
+ stateOrProvinceName = Some-state
+ localityName = Some-city
+ 0.organizationName = Some-org
+ CN = $cnname
+ [ req_attributes ]
+ challengePassword = foobar
+ unstructuredName = An optional company name
+ [ usr_cert ]
+ basicConstraints=CA:FALSE
+ nsComment = "OpenSSL Generated Certificate"
+ subjectKeyIdentifier=hash
+ authorityKeyIdentifier=keyid,issuer
+ [ v3_req ]
+ basicConstraints = CA:FALSE
+ keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+ [ v3_ca ]
+ subjectKeyIdentifier=hash
+ authorityKeyIdentifier=keyid:always,issuer
+ basicConstraints = CA:true
+ OPENSSL_CNF
+
+ # though we use a password, the key is discarded and never used
+ openssl req -batch -passout pass:"$certpass" -new -x509 \
+ -keyout cakey.pem -out cacert.pem -days 3650 \
+ -config openssl.cnf -newkey rsa:2048 >/dev/null 2>&1 &&
+
+ # make new certificate
+ openssl req -batch -nodes -new -x509 -keyout newkey.pem \
+ -out newreq.pem -days 365 -config openssl.cnf \
+ -newkey rsa:2048 >/dev/null 2>&1 &&
+
+ # sign certificate
+ openssl x509 -x509toreq -in newreq.pem -signkey newkey.pem \
+ -out tmp.pem >/dev/null 2>&1 &&
+ openssl ca -notext -config openssl.cnf \
+ -out newcert.pem -keyfile cakey.pem -cert cacert.pem \
+ -key "$certpass" -batch -infiles tmp.pem >/dev/null 2>&1 &&
+
+ mkdir -p "$CERTDIR" &&
+ chmod 0755 "$CERTDIR" &&
+ chmod 644 newcert.pem cacert.pem &&
+ chmod 600 newkey.pem &&
+ cp -p newcert.pem "$CERTDIR"/host.cert &&
+ cp -p cacert.pem "$CERTDIR"/cacert.pem &&
+ cp -p newkey.pem "$CERTDIR"/host.key &&
+ ln -s cacert.pem "$CERTDIR"/`openssl x509 -hash -noout \
+ -in cacert.pem`.0)
+
+ retVal="$?"
+ rm -rf "$CAdir"
+
+ return "$retVal"
+}
+
+sendmail_precmd()
+{
+ # check modifications on /etc/mail/aliases
+ if checkyesno sendmail_rebuild_aliases; then
+ if [ -f "/etc/mail/aliases.db" ]; then
+ if [ "/etc/mail/aliases" -nt "/etc/mail/aliases.db" ]; then
+ echo \
+ "${name}: /etc/mail/aliases newer than /etc/mail/aliases.db, regenerating"
+ /usr/bin/newaliases
+ fi
+ else
+ echo \
+ "${name}: /etc/mail/aliases.db not present, generating"
+ /usr/bin/newaliases
+ fi
+ fi
+
+ if checkyesno sendmail_cert_create && [ ! \( \
+ -f "$CERTDIR/host.cert" -o -f "$CERTDIR/host.key" -o \
+ -f "$CERTDIR/cacert.pem" \) ]; then
+ if ! openssl version >/dev/null 2>&1; then
+ warn "OpenSSL not available, but sendmail_cert_create is YES."
+ else
+ info Creating certificate for sendmail.
+ sendmail_cert_create
+ fi
+ fi
+
+ if [ ! -f /var/log/sendmail.st ]; then
+ /usr/bin/install -m 640 -o root -g wheel /dev/null /var/log/sendmail.st
+ fi
+}
+
+if ${_sendmail_run:-false}; then
+ run_rc_command "$1"
+fi
+_ret=$?
+
+if ${_sendmail_msp_queue_run:-false}; then
+ name="sendmail_msp_queue"
+ rcvar="sendmail_msp_queue_enable"
+ pidfile="${sendmail_msp_queue_pidfile:-/var/spool/clientmqueue/sm-client.pid}"
+ required_files="/etc/mail/submit.cf"
+ _rc_restart_done=false
+ run_rc_command "$1"
+ _ret=$(( _ret > $? ? _ret : $? ))
+fi
+
+(exit "$_ret")
diff --git a/libexec/rc/rc.d/serial b/libexec/rc/rc.d/serial
new file mode 100755
index 000000000000..f8ddc7ff30d4
--- /dev/null
+++ b/libexec/rc/rc.d/serial
@@ -0,0 +1,158 @@
+#!/bin/sh
+#
+# Copyright (c) 1996 Andrey A. Chernov
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: serial
+# REQUIRE: root
+# KEYWORD: nojail
+
+# Change some defaults for serial devices.
+# Standard defaults are:
+# dtrwait 300 drainwait `sysctl -n kern.drainwait`
+# initial cflag from <sys/ttydefaults.h> = cread cs8 hupcl
+# initial iflag, lflag and oflag all 0
+# speed 115200
+# special chars from <sys/ttydefaults.h>
+# nothing locked
+# except for serial consoles the initial iflag, lflag and oflag are from
+# <sys/ttydefaults.h> and clocal is locked on.
+
+default() {
+ # Reset everything changed by the other functions to initial defaults.
+
+ dc=$1; shift # device name character
+ drainwait=`sysctl -n kern.tty_drainwait`
+
+ for i in $*
+ do
+ comcontrol /dev/tty${dc}${i} dtrwait 300 drainwait $drainwait
+ stty < /dev/tty${dc}${i}.init -clocal crtscts hupcl 115200 reprint ^R
+ stty < /dev/tty${dc}${i}.lock -clocal -crtscts -hupcl 0
+ stty < /dev/cua${dc}${i}.init -clocal crtscts hupcl 115200 reprint ^R
+ stty < /dev/cua${dc}${i}.lock -clocal -crtscts -hupcl 0
+ done
+}
+
+maybe() {
+ # Special settings.
+
+ dc=$1; shift
+
+ for i in $*
+ do
+ # Don't use ^R; it breaks bash's ^R when typed ahead.
+ stty < /dev/tty${dc}${i}.init reprint undef
+ stty < /dev/cua${dc}${i}.init reprint undef
+ # Lock clocal off on dialin device for security.
+ stty < /dev/tty${dc}${i}.lock clocal
+ # Lock the speeds to use old binaries that don't support them.
+ # Any legal speed works to lock the initial speed.
+ stty < /dev/tty${dc}${i}.lock 300
+ stty < /dev/cua${dc}${i}.lock 300
+ done
+}
+
+modem() {
+ # Modem that supports CTS and perhaps RTS handshaking.
+
+ dc=$1; shift
+
+ for i in $*
+ do
+ # may depend on modem
+ comcontrol /dev/tty${dc}${i} drainwait 180
+ # Lock crtscts on.
+ # Speed reasonable for V42bis.
+ stty < /dev/tty${dc}${i}.init crtscts 115200
+ stty < /dev/tty${dc}${i}.lock crtscts
+ stty < /dev/cua${dc}${i}.init crtscts 115200
+ stty < /dev/cua${dc}${i}.lock crtscts
+ done
+}
+
+mouse() {
+ # Mouse on either callin or callout port.
+
+ dc=$1; shift
+
+ for i in $*
+ do
+ # Lock clocal on, hupcl off.
+ # Standard speed for Microsoft mouse.
+ stty < /dev/tty${dc}${i}.init clocal -hupcl 1200
+ stty < /dev/tty${dc}${i}.lock clocal hupcl
+ stty < /dev/cua${dc}${i}.init clocal -hupcl 1200
+ stty < /dev/cua${dc}${i}.lock clocal hupcl
+ done
+}
+
+terminal() {
+ # Terminal that supports CTS and perhaps RTS handshaking
+ # with the cable or terminal arranged so that DCD is on
+ # at least while the terminal is on.
+ # Also works for bidirectional communications to another pc
+ # provided at most one side runs getty.
+ # Same as modem() except we want a faster speed and no dtrwait.
+
+ dc=$1; shift
+
+ modem ${dc} $*
+ for i in $*
+ do
+ comcontrol /dev/tty${dc}${i} dtrwait 0
+ stty < /dev/tty${dc}${i}.init 115200
+ stty < /dev/cua${dc}${i}.init 115200
+ done
+}
+
+3wire() {
+ # 3-wire serial terminals. These don't supply carrier, so
+ # clocal needs to be set, and crtscts needs to be unset.
+
+ dc=$1; shift
+
+ terminal ${dc} $*
+ for i in $*
+ do
+ stty < /dev/tty${dc}${i}.init clocal -crtscts
+ stty < /dev/cua${dc}${i}.init clocal -crtscts
+ done
+}
+
+# Don't use anything from this file unless you have some buggy programs
+# that require it.
+
+# Edit the functions and the examples to suit your system.
+# $1 is the device identifier, and the remainder of the line
+# lists the device numbers.
+
+# Initialize assorted 8250-16550 (uart) ports.
+# maybe u 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v
+# mouse u 2
+# modem u 1
+# terminal u 0
+# 3wire u 0
diff --git a/libexec/rc/rc.d/sshd b/libexec/rc/rc.d/sshd
new file mode 100755
index 000000000000..1d2c89cc88a8
--- /dev/null
+++ b/libexec/rc/rc.d/sshd
@@ -0,0 +1,87 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: sshd
+# REQUIRE: LOGIN FILESYSTEMS
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="sshd"
+desc="Secure Shell Daemon"
+rcvar="sshd_enable"
+command="/usr/sbin/${name}"
+keygen_cmd="sshd_keygen"
+start_precmd="sshd_precmd"
+reload_precmd="sshd_configtest"
+restart_precmd="sshd_configtest"
+configtest_cmd="sshd_configtest"
+pidfile="/var/run/${name}.pid"
+extra_commands="configtest keygen reload"
+
+: ${sshd_rsa_enable:="yes"}
+: ${sshd_ecdsa_enable:="yes"}
+: ${sshd_ed25519_enable:="yes"}
+
+# sshd in a jail would not see other jails. As such exclude it from
+# svcj_all_enable="YES" by setting sshd_svcj to NO. This allows to
+# enable it in rc.conf.
+: ${sshd_svcj:="NO"}
+: ${sshd_svcj_options:="net_basic"}
+
+sshd_keygen_alg()
+{
+ local alg=$1
+ local ALG="$(echo $alg | tr a-z A-Z)"
+ local keyfile
+
+ if ! checkyesno "sshd_${alg}_enable" ; then
+ return 0
+ fi
+
+ case $alg in
+ rsa|ecdsa|ed25519)
+ keyfile="/etc/ssh/ssh_host_${alg}_key"
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+
+ if [ -f "${keyfile}" ] ; then
+ info "$ALG host key exists."
+ return 0
+ fi
+
+ if [ ! -x /usr/bin/ssh-keygen ] ; then
+ warn "/usr/bin/ssh-keygen does not exist."
+ return 1
+ fi
+
+ echo "Generating $ALG host key."
+ /usr/bin/ssh-keygen -q -t $alg -f "$keyfile" -N ""
+ /usr/bin/ssh-keygen -l -f "$keyfile.pub"
+}
+
+sshd_keygen()
+{
+ sshd_keygen_alg rsa
+ sshd_keygen_alg ecdsa
+ sshd_keygen_alg ed25519
+}
+
+sshd_configtest()
+{
+ echo "Performing sanity check on ${name} configuration."
+ eval ${command} ${sshd_flags} -t
+}
+
+sshd_precmd()
+{
+ run_rc_command keygen
+ run_rc_command configtest
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/statd b/libexec/rc/rc.d/statd
new file mode 100755
index 000000000000..3f2678af2940
--- /dev/null
+++ b/libexec/rc/rc.d/statd
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# FreeBSD History: src/etc/rc.d/nfslocking,v 1.11 2004/10/07 13:55:26 mtm Exp
+#
+
+# PROVIDE: statd
+# REQUIRE: nfsclient rpcbind
+# BEFORE: DAEMON
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="statd"
+desc="host status monitoring daemon"
+rcvar=rpc_statd_enable
+command="/usr/sbin/rpc.${name}"
+start_precmd='statd_precmd'
+
+: ${statd_svcj_options:="net_basic"}
+
+# Make sure that we are either an NFS client or server, and that we get
+# the correct flags from rc.conf(5).
+#
+statd_precmd()
+{
+ force_depend rpcbind || return 1
+}
+
+load_rc_config $name
+
+rc_flags=${rpc_statd_flags}
+
+run_rc_command $1
diff --git a/libexec/rc/rc.d/static_arp b/libexec/rc/rc.d/static_arp
new file mode 100755
index 000000000000..42db3c2c8fff
--- /dev/null
+++ b/libexec/rc/rc.d/static_arp
@@ -0,0 +1,77 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Xin LI <delphij@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Configure static ARP table
+#
+#
+
+# PROVIDE: static_arp
+# REQUIRE: netif
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="static_arp"
+desc="Static ARP Configuration"
+start_cmd="static_arp_start"
+stop_cmd="static_arp_stop"
+
+static_arp_start()
+{
+ local e arp_args
+
+ if [ -n "${static_arp_pairs}" ]; then
+ echo -n 'Binding static ARP pair(s):'
+ for e in ${static_arp_pairs}; do
+ echo -n " ${e}"
+ eval arp_args=\$static_arp_${e}
+ arp -S ${arp_args} >/dev/null 2>&1
+ done
+ echo '.'
+ fi
+}
+
+static_arp_stop()
+{
+ local e arp_args
+
+ if [ -n "${static_arp_pairs}" ]; then
+ echo -n 'Unbinding static ARP pair(s):'
+ for e in ${static_arp_pairs}; do
+ echo -n " ${e}"
+ eval arp_args=\$static_arp_${e}
+ arp -d ${arp_args%%[ ]*} > /dev/null 2>&1
+ done
+ echo '.'
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+statc_arp_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/static_ndp b/libexec/rc/rc.d/static_ndp
new file mode 100755
index 000000000000..e66c4a0080c3
--- /dev/null
+++ b/libexec/rc/rc.d/static_ndp
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 Xin LI <delphij@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Configure static NDP table
+#
+#
+
+# PROVIDE: static_ndp
+# REQUIRE: netif
+# KEYWORD: nojailvnet
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="static_ndp"
+start_cmd="static_ndp_start"
+stop_cmd="static_ndp_stop"
+
+static_ndp_start()
+{
+ local e ndp_args
+
+ if [ -n "${static_ndp_pairs}" ]; then
+ echo -n 'Binding static NDP pair(s):'
+ for e in ${static_ndp_pairs}; do
+ echo -n " ${e}"
+ eval ndp_args=\$static_ndp_${e}
+ ndp -s ${ndp_args} >/dev/null 2>&1
+ done
+ echo '.'
+ fi
+}
+
+static_ndp_stop()
+{
+ local e ndp_args
+
+ if [ -n "${static_ndp_pairs}" ]; then
+ echo -n 'Unbinding static NDP pair(s):'
+ for e in ${static_ndp_pairs}; do
+ echo -n " ${e}"
+ eval ndp_args=\$static_ndp_${e}
+ ndp -d ${ndp_args%%[ ]*} > /dev/null 2>&1
+ done
+ echo '.'
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+static_ndp_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/stf b/libexec/rc/rc.d/stf
new file mode 100755
index 000000000000..94a585693982
--- /dev/null
+++ b/libexec/rc/rc.d/stf
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+
+# PROVIDE: stf
+# REQUIRE: netif
+# KEYWORD: nojail
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="stf"
+desc="6to4 tunnel interface"
+start_cmd="stf_up"
+stop_cmd="stf_down"
+
+stf_up()
+{
+ case ${stf_interface_ipv4addr} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ # assign IPv6 addr and interface route for 6to4 interface
+ stf_prefixlen=$((16+${stf_interface_ipv4plen:-0}))
+ OIFS="$IFS"
+ IFS=".$IFS"
+ set ${stf_interface_ipv4addr}
+ IFS="$OIFS"
+ hexfrag1=`hexprint $(($1*256 + $2))`
+ hexfrag2=`hexprint $(($3*256 + $4))`
+ ipv4_in_hexformat="${hexfrag1}:${hexfrag2}"
+ case ${stf_interface_ipv6_ifid} in
+ [Aa][Uu][Tt][Oo] | '')
+ for i in ${ipv6_network_interfaces}; do
+ laddr=`network6_getladdr ${i}`
+ case ${laddr} in
+ '')
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+ stf_interface_ipv6_ifid=`expr "${laddr}" : \
+ 'fe80::\(.*\)%\(.*\)'`
+ case ${stf_interface_ipv6_ifid} in
+ '')
+ stf_interface_ipv6_ifid=0:0:0:1
+ ;;
+ esac
+ ;;
+ esac
+ echo "Configuring 6to4 tunnel interface: stf0."
+ ifconfig stf0 create >/dev/null 2>&1
+ ifconfig stf0 inet6 2002:${ipv4_in_hexformat}:${stf_interface_ipv6_slaid:-0}:${stf_interface_ipv6_ifid} \
+ prefixlen ${stf_prefixlen}
+ check_startmsgs && /sbin/ifconfig stf0
+
+ # disallow packets to malicious 6to4 prefix
+ route add -inet6 2002:e000:: -prefixlen 20 ::1 -reject
+ route add -inet6 2002:7f00:: -prefixlen 24 ::1 -reject
+ route add -inet6 2002:0000:: -prefixlen 24 ::1 -reject
+ route add -inet6 2002:ff00:: -prefixlen 24 ::1 -reject
+ ;;
+ esac
+}
+
+stf_down()
+{
+ echo "Removing 6to4 tunnel interface: stf0."
+ ifconfig stf0 destroy
+ route delete -inet6 2002:e000:: -prefixlen 20 ::1
+ route delete -inet6 2002:7f00:: -prefixlen 24 ::1
+ route delete -inet6 2002:0000:: -prefixlen 24 ::1
+ route delete -inet6 2002:ff00:: -prefixlen 24 ::1
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+stf_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/swap b/libexec/rc/rc.d/swap
new file mode 100755
index 000000000000..f7663fc422bf
--- /dev/null
+++ b/libexec/rc/rc.d/swap
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: swap
+# REQUIRE: disks
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="swap"
+desc="Setup swap space"
+start_cmd='/sbin/swapon -aq'
+stop_cmd=':'
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: privileged operations
+swap_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/swaplate b/libexec/rc/rc.d/swaplate
new file mode 100755
index 000000000000..da86cb2bf686
--- /dev/null
+++ b/libexec/rc/rc.d/swaplate
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: swaplate
+# REQUIRE: mountlate
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="swaplate"
+desc="Setup late swap space"
+start_cmd='/sbin/swapon -aLq'
+stop_cmd='/sbin/swapoff -aLq'
+
+load_rc_config swap
+
+# doesn't make sense to run in a svcj: privileged operations
+swaplate_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/syscons b/libexec/rc/rc.d/syscons
new file mode 100755
index 000000000000..b01b648ace6e
--- /dev/null
+++ b/libexec/rc/rc.d/syscons
@@ -0,0 +1,406 @@
+#!/bin/sh -
+#
+# Copyright (c) 2000 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: syscons
+# REQUIRE: LOGIN
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="syscons"
+desc="Configure the system console"
+extra_commands="setkeyboard"
+setkeyboard_cmd="syscons_setkeyboard"
+start_precmd="syscons_precmd"
+start_cmd="syscons_start"
+stop_cmd=":"
+
+# stdin must be redirected because it might be for a serial console
+#
+kbddev=/dev/ttyv0
+viddev=/dev/ttyv0
+
+_sc_config=
+_sc_console=
+_sc_initdone=
+_sc_keymap_msg=
+_sc_bootmethod=
+sc_init()
+{
+ local bootmethod
+
+ if [ -z "${_sc_initdone}" ]; then
+ if [ -z "${_sc_console}" ]; then
+ if [ x`sysctl -n kern.vty` = x"vt" ]; then
+ _sc_console="vt"
+ else
+ _sc_console="syscons"
+ fi
+ _sc_config="${_sc_console}"
+ fi
+ if [ -z "${_sc_bootmethod}" ]; then
+ bootmethod=$(sysctl -qn machdep.bootmethod)
+ case ${bootmethod} in
+ UEFI)
+ _sc_bootmethod="uefi"
+ ;;
+ BIOS)
+ _sc_bootmethod="bios"
+ ;;
+ PVH)
+ _sc_bootmethod="pvh"
+ ;;
+ *)
+ _sc_bootmethod="uefi" # Default to UEFI
+ ;;
+ esac
+ fi
+ echo -n "Configuring ${_sc_config}:"
+ _sc_initdone=yes
+ fi
+}
+
+# syscons to vt migration helper
+lookup_keymap_for_vt()
+{
+ keymap=`basename $1 .kbd`
+ case $keymap in
+hy.armscii-8) echo am;;
+be.iso.acc) echo be.acc;;
+be.iso) echo be;;
+bg.bds.ctrlcaps) echo bg.bds;;
+bg.phonetic.ctrlcaps) echo bg.phonetic;;
+br275.iso.acc) echo br;;
+br275.*) echo br.noacc;;
+by.*) echo by;;
+fr_CA.iso.acc) echo ca-fr;;
+swissgerman.macbook.acc) echo ch.macbook.acc;;
+swissgerman.iso.acc) echo ch.acc;;
+swissgerman.*) echo ch;;
+swissfrench.iso.acc) echo ch-fr.acc;;
+swissfrench.*) echo ch-fr;;
+ce.iso2) echo centraleuropean.qwerty;;
+colemak.iso15.acc) echo colemak.acc;;
+cs.*|cz.*) echo cz;;
+german.iso.acc) echo de.acc;;
+german.*) echo de;;
+danish.iso.acc) echo dk.acc;;
+danish.iso.macbook) echo dk.macbook;;
+danish.*) echo dk;;
+estonian.*) echo ee;;
+spanish.dvorak) echo es.dvorak;;
+spanish.iso*.acc) echo es.acc;;
+spanish.iso) echo es;;
+finnish.*) echo fi;;
+fr.macbook.acc) echo fr.macbook;;
+fr.iso.acc) echo fr.acc;;
+fr.iso) echo fr;;
+el.iso07) echo gr;;
+gr.us101.acc) echo gr.101.acc;;
+hr.iso) echo hr;;
+hu.iso2.101keys) echo hu.101;;
+hu.iso2.102keys) echo hu.102;;
+iw.iso8) echo il;;
+icelandic.iso.acc) echo is.acc;;
+icelandic.iso) echo is;;
+it.iso) echo it;;
+jp.106x) echo jp.capsctrl;;
+jp.106) echo jp;;
+kk.pt154.io) echo kz.io;;
+kk.pt154.kst) echo kz.kst;;
+latinamerican.iso.acc) echo latinamerican.acc;;
+lt.iso4) echo lt;;
+norwegian.iso) echo no;;
+norwegian.dvorak) echo no.dvorak;;
+dutch.iso.acc) echo nl;;
+eee_nordic) echo nordic.asus-eee;;
+pl_PL.dvorak) echo pl.dvorak;;
+pl_PL.ISO8859-2) echo pl;;
+pt.iso.acc) echo pt.acc;;
+pt.iso) echo pt;;
+ru.koi8-r.shift) echo ru.shift;;
+ru.koi8-r.win) echo ru.win;;
+ru.*) echo ru;;
+swedish.*) echo se;;
+si.iso) echo si;;
+sk.iso2) echo sk;;
+tr.iso9.q) echo tr;;
+ua.koi8-u.shift.alt) echo ua.shift.alt;;
+ua.*) echo ua;;
+uk.*-ctrl) echo uk.capsctrl;;
+uk.dvorak) echo uk.dvorak;;
+uk.*) echo uk;;
+us.iso.acc) echo us.acc;;
+us.pc-ctrl) echo us.ctrl;;
+us.iso) echo us;;
+ esac
+}
+
+kbdcontrol_load_keymap()
+{
+ errmsg=`kbdcontrol < ${kbddev} -l ${keymap} 2>&1`
+ if [ -n "${errmsg}" -a "${_sc_console}" = "vt" ]; then
+ _sc_keymap_msg="${errmsg}"
+ keymap_vt=`lookup_keymap_for_vt ${keymap}`
+ if [ -n "${keymap_vt}" ]; then
+ errmsg=`kbdcontrol < ${kbddev} -l ${keymap_vt} 2>&1`
+ if [ -z "${errmsg}" ]; then
+ _sc_keymap_msg="New keymap: In /etc/rc.conf replace 'keymap=${keymap}' by 'keymap=${keymap_vt}'"
+ fi
+ else
+ _sc_keymap_msg="No replacement found for keymap '${keymap}'.
+You may try to convert your keymap file using 'convert-keymap.pl', which is
+part of the system sources and located in /usr/src/tools/tools/vt/keymaps/"
+ fi
+ fi
+}
+
+# helper
+syscons_configure_keyboard()
+{
+ # keymap
+ #
+ case ${keymap} in
+ NO | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' keymap'; kbdcontrol_load_keymap
+ ;;
+ esac
+
+ # keyrate
+ #
+ case ${keyrate} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' keyrate'; kbdcontrol < ${kbddev} -r ${keyrate}
+ ;;
+ esac
+
+ # keybell
+ #
+ case ${keybell} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' keybell'; kbdcontrol < ${kbddev} -b ${keybell}
+ ;;
+ esac
+
+ # change function keys
+ #
+ case ${keychange} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' keychange'
+ set -- ${keychange}
+ while [ $# -gt 0 ]; do
+ kbdcontrol <${kbddev} -f "$1" "$2"
+ shift; shift
+ done
+ ;;
+ esac
+
+ # set this keyboard mode for all virtual terminals
+ #
+ if [ -n "${allscreens_kbdflags}" ]; then
+ sc_init
+ echo -n ' allscreens_kbd'
+ for ttyv in /dev/ttyv*; do
+ [ "$ttyv" = '/dev/ttyv*' ] && break
+ kbdcontrol ${allscreens_kbdflags} < ${ttyv} > ${ttyv} 2>&1
+ done
+ fi
+}
+
+syscons_setkeyboard()
+{
+ kbd=$1
+
+ if [ -z "${kbd}" ]; then
+ return 1
+ fi
+
+ # Check if the kbdmux(4) is the current active keyboard
+ kbdcontrol -i < ${kbddev} | grep kbdmux > /dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ kbdcontrol -k ${kbd} < ${kbddev} > /dev/null 2>&1
+ fi
+
+ _sc_config="keyboard"
+ syscons_configure_keyboard
+
+ # Terminate keyboard configuration line and reset global variables.
+ #
+ if [ -n "${_sc_initdone}" ]; then
+ echo '.'
+ _sc_config="${_sc_console}"
+ _sc_initdone=
+ fi
+}
+
+syscons_precmd()
+{
+ if [ ! -c $kbddev ]
+ then
+ return 1
+ fi
+ return 0
+}
+
+syscons_bios_start()
+{
+ # cursor type
+ #
+ case ${cursor} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' cursor'; vidcontrol < ${viddev} -c ${cursor}
+ ;;
+ esac
+
+ # screen mapping
+ #
+ case ${scrnmap} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' scrnmap'; vidcontrol < ${viddev} -l ${scrnmap}
+ ;;
+ esac
+
+ # blank time
+ #
+ case ${blanktime} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' blanktime'; vidcontrol < ${viddev} -t ${blanktime}
+ ;;
+ esac
+
+ # screen saver
+ #
+ case ${saver} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' screensaver'
+ for i in `kldstat | awk '$5 ~ "_saver\.ko$" { print $5 }'`; do
+ kldunload ${i}
+ done
+ load_kld -e _saver ${saver}_saver
+ ;;
+ esac
+}
+
+syscons_start()
+{
+ # keyboard
+ #
+ if [ -n "${keyboard}" ]; then
+ syscons_setkeyboard ${keyboard}
+ fi
+
+ syscons_configure_keyboard
+
+ if [ "${_sc_bootmethod}" = "bios" ]; then
+ syscons_bios_start
+ fi
+
+ # font 8x16
+ #
+ case ${font8x16} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' font8x16'; vidcontrol < ${viddev} -f 8x16 ${font8x16}
+ ;;
+ esac
+
+ # font 8x14
+ #
+ case ${font8x14} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' font8x14'; vidcontrol < ${viddev} -f 8x14 ${font8x14}
+ ;;
+ esac
+
+ # font 8x8
+ #
+ case ${font8x8} in
+ [Nn][Oo] | '')
+ ;;
+ *)
+ sc_init
+ echo -n ' font8x8'; vidcontrol < ${viddev} -f 8x8 ${font8x8}
+ ;;
+ esac
+
+ # set this mode for all virtual screens
+ #
+ if [ -n "${allscreens_flags}" ]; then
+ sc_init
+ echo -n ' allscreens'
+ for ttyv in /dev/ttyv*; do
+ [ "$ttyv" = '/dev/ttyv*' ] && break
+ vidcontrol ${allscreens_flags} < ${ttyv} > ${ttyv} 2>&1
+ done
+ fi
+
+ [ -n "${_sc_initdone}" ] && echo '.'
+ if [ -n "${_sc_keymap_msg}" ]; then
+ echo
+ echo "WARNING:"
+ echo "${_sc_keymap_msg}."
+ echo
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+syscons_svcj="NO"
+
+run_rc_command $*
+
diff --git a/libexec/rc/rc.d/sysctl b/libexec/rc/rc.d/sysctl
new file mode 100755
index 000000000000..0ca753b530af
--- /dev/null
+++ b/libexec/rc/rc.d/sysctl
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: sysctl
+
+. /etc/rc.subr
+
+name="sysctl"
+desc="Set sysctl variables from /etc/sysctl.conf and /etc/sysctl.conf.local"
+command="/sbin/sysctl"
+stop_cmd=":"
+start_cmd="sysctl_start"
+reload_cmd="sysctl_start last"
+lastload_cmd="sysctl_start last"
+extra_commands="reload lastload"
+
+sysctl_start()
+{
+ case $1 in
+ last)
+ command_args="-f"
+ ;;
+ *)
+ command_args="-i -f"
+ ;;
+ esac
+
+ for _f in /etc/sysctl.conf /etc/sysctl.conf.local; do
+ if [ -r ${_f} ]; then
+ ${command} ${command_args} ${_f} > /dev/null
+ fi
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+sysctl_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/sysctl_lastload b/libexec/rc/rc.d/sysctl_lastload
new file mode 100755
index 000000000000..6d97561ed2c0
--- /dev/null
+++ b/libexec/rc/rc.d/sysctl_lastload
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: sysctl_lastload
+# REQUIRE: LOGIN
+# BEFORE: jail
+
+. /etc/rc.subr
+
+name="sysctl_lastload"
+desc="Last chance to set sysctl variables that failed the first time."
+start_cmd="/etc/rc.d/sysctl lastload"
+stop_cmd=":"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+sysctl_lastload_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/syslogd b/libexec/rc/rc.d/syslogd
new file mode 100755
index 000000000000..8d0ff952a6b2
--- /dev/null
+++ b/libexec/rc/rc.d/syslogd
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+#
+
+# netif is required for lo0 because syslogd tries to open a local socket
+#
+# PROVIDE: syslogd
+# REQUIRE: mountcritremote FILESYSTEMS newsyslog netif
+# BEFORE: SERVERS
+
+. /etc/rc.subr
+
+name="syslogd"
+desc="System log daemon"
+rcvar="syslogd_enable"
+pidfile="/var/run/syslog.pid"
+command="/usr/sbin/${name}"
+required_files="/etc/syslog.conf"
+start_precmd="syslogd_precmd"
+extra_commands="reload"
+
+sockfile="/var/run/syslogd.sockets"
+evalargs="rc_flags=\"\`set_socketlist\` \$rc_flags\""
+
+: ${syslogd_svcj_options:="net_basic"}
+
+syslogd_precmd()
+{
+ local _l _ldir
+
+ # Transitional symlink for old binaries
+ #
+ if [ ! -L /dev/log ] && ! check_jail jailed; then
+ ln -sf /var/run/log /dev/log
+ fi
+ rm -f /var/run/log
+
+ # Create default list of syslog sockets to watch
+ #
+ ( umask 022 ; > $sockfile )
+
+ # If running named(8) or ntpd(8) chrooted, added appropriate
+ # syslog socket to list of sockets to watch.
+ #
+ for _l in $altlog_proglist; do
+ eval _ldir=\$${_l}_chrootdir
+ if checkyesno ${_l}_enable && [ -n "$_ldir" ]; then
+ echo "${_ldir}/var/run/log" >> $sockfile
+ fi
+ done
+
+ # If other sockets have been provided, change run_rc_command()'s
+ # internal copy of $syslogd_flags to force use of specific
+ # syslogd sockets.
+ #
+ if [ -s $sockfile ]; then
+ echo "/var/run/log" >> $sockfile
+ eval $evalargs
+ fi
+
+ return 0
+}
+
+set_socketlist()
+{
+ local _s _socketargs
+
+ _socketargs=
+ for _s in `cat $sockfile | tr '\n' ' '` ; do
+ _socketargs="-l $_s $_socketargs"
+ done
+ echo $_socketargs
+}
+load_rc_config $name
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/sysvipc b/libexec/rc/rc.d/sysvipc
new file mode 100755
index 000000000000..ce38db598641
--- /dev/null
+++ b/libexec/rc/rc.d/sysvipc
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: sysvipc
+# REQUIRE: kldxref
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="sysvipc"
+desc="Load SysV IPC modules"
+rcvar="sysvipc_enable"
+start_cmd="${name}_start"
+stop_cmd=":"
+
+sysvipc_start()
+{
+ load_kld sysvmsg
+ load_kld sysvsem
+ load_kld sysvshm
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: privileged operations
+sysvipc_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/tlsclntd b/libexec/rc/rc.d/tlsclntd
new file mode 100755
index 000000000000..5688c7ff53a2
--- /dev/null
+++ b/libexec/rc/rc.d/tlsclntd
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: tlsclntd
+# REQUIRE: NETWORKING root mountcritlocal sysctl
+# BEFORE: nfscbd
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="tlsclntd"
+desc="NFS over TLS client side daemon"
+rcvar="tlsclntd_enable"
+command="/usr/sbin/rpc.${name}"
+pidfile="/var/run/rpc.${name}.pid"
+
+: ${tlsclntd_svcj_options:="net_basic"}
+
+load_rc_config $name
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/tlsservd b/libexec/rc/rc.d/tlsservd
new file mode 100755
index 000000000000..989e17996043
--- /dev/null
+++ b/libexec/rc/rc.d/tlsservd
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: tlsservd
+# REQUIRE: NETWORKING root mountcritlocal sysctl
+# BEFORE: nfsd
+# KEYWORD: nojailvnet shutdown
+
+. /etc/rc.subr
+
+name="tlsservd"
+desc="NFS over TLS server side daemon"
+rcvar="tlsservd_enable"
+command="/usr/sbin/rpc.${name}"
+
+: ${tlsservd_svcj_options:="net_basic nfsd"}
+
+pidfile="/var/run/rpc.${name}.pid"
+required_files="/etc/rpc.tlsservd/cert.pem /etc/rpc.tlsservd/certkey.pem"
+extra_commands="reload"
+
+
+load_rc_config $name
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/tmp b/libexec/rc/rc.d/tmp
new file mode 100755
index 000000000000..cc970816e45c
--- /dev/null
+++ b/libexec/rc/rc.d/tmp
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# Copyright (c) 1999 Matt Dillon
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: tmp
+# REQUIRE: mountcritlocal
+
+. /etc/rc.subr
+
+name="tmp"
+desc="Configure tmpfs"
+stop_cmd=':'
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: mounting
+tmp_svcj="NO"
+
+mount_tmpmfs()
+{
+ while read line; do
+ case $line in
+ /dev/md[0-9]*\ /tmp)
+ return;;
+ esac
+ done <<*EOF
+$(df /tmp)
+*EOF
+ mount_md ${tmpsize} /tmp "${tmpmfs_flags}"
+ chmod 01777 /tmp
+}
+
+# If we do not have a writable /tmp, create a memory
+# filesystem for /tmp. If /tmp is a symlink (e.g. to /var/tmp,
+# then it should already be writable).
+#
+case "${tmpmfs}" in
+[Aa][Uu][Tt][Oo])
+ _tmpdir=/tmp/.diskless.$(dd if=/dev/random bs=32 count=1 status=none | sha256)
+ if mkdir -m 0700 ${_tmpdir}; then
+ rmdir ${_tmpdir}
+ else
+ if [ -h /tmp ]; then
+ echo "*** /tmp is a symlink to a non-writable area!"
+ echo "dropping into shell, ^D to continue anyway."
+ /bin/sh
+ else
+ mount_tmpmfs
+ fi
+ fi
+ ;;
+*)
+ if checkyesno tmpmfs; then
+ mount_tmpmfs
+ fi
+ ;;
+esac
diff --git a/libexec/rc/rc.d/ubthidhci b/libexec/rc/rc.d/ubthidhci
new file mode 100755
index 000000000000..9792a0e3530d
--- /dev/null
+++ b/libexec/rc/rc.d/ubthidhci
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ubthidhci
+# REQUIRE: DAEMON
+# BEFORE: bluetooth
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="ubthidhci"
+rcvar="ubthidhci_enable"
+command="/usr/sbin/usbconfig"
+start_precmd="ubthidhci_prestart"
+
+ubthidhci_prestart()
+{
+
+ if [ -z ${ubthidhci_busnum} ]; then
+ warn ubthidhci_busnum is not set
+ return 1
+ fi
+ if [ -z ${ubthidhci_addr} ]; then
+ warn ubthidhci_addr is not set
+ return 1
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+ubthidhci_svcj="NO"
+
+#
+# We discard the output because:
+# 1) we don't want it to show up during boot; and
+# 2) the request usually returns an error, but that doesn't mean it failed
+#
+# NB: 0x40 is UT_VENDOR
+command_args="-u ${ubthidhci_busnum} -a ${ubthidhci_addr} do_request 0x40 0 0 0 0 > /dev/null 2>&1"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ugidfw b/libexec/rc/rc.d/ugidfw
new file mode 100755
index 000000000000..13b20c45ee29
--- /dev/null
+++ b/libexec/rc/rc.d/ugidfw
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+
+# PROVIDE: ugidfw
+# REQUIRE: FILESYSTEMS
+# BEFORE: LOGIN
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="ugidfw"
+desc="Firewall-like access controls for file system objects"
+rcvar="ugidfw_enable"
+start_cmd="ugidfw_start"
+stop_cmd="ugidfw_stop"
+required_modules="mac_bsdextended"
+
+ugidfw_load()
+{
+ if [ -r "${bsdextended_script}" ]; then
+ . "${bsdextended_script}"
+ fi
+}
+
+ugidfw_start()
+{
+ [ -z "${bsdextended_script}" ] && bsdextended_script=/etc/rc.bsdextended
+
+ if [ -r "${bsdextended_script}" ]; then
+ ugidfw_load
+ echo "MAC bsdextended rules loaded."
+ fi
+}
+
+ugidfw_stop()
+{
+ local rulecount
+
+ # Disable the policy
+ #
+ # Check for the existence of rules and flush them if needed.
+ rulecount=$(sysctl -in security.mac.bsdextended.rule_count)
+ if [ ${rulecount:-0} -gt 0 ]; then
+ ugidfw list | sed -n '2,$p' | cut -d ' ' -f 1 | sort -r -n |
+ xargs -n 1 ugidfw remove
+ echo "MAC bsdextended rules flushed."
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: nojail keyword
+ugidfw_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/utx b/libexec/rc/rc.d/utx
new file mode 100755
index 000000000000..d7149f66e68b
--- /dev/null
+++ b/libexec/rc/rc.d/utx
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: utx
+# REQUIRE: DAEMON FILESYSTEMS
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="utx"
+desc="Manage the user accounting database"
+rcvar="utx_enable"
+start_cmd="utx boot"
+stop_cmd="utx shutdown"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+utx_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/var b/libexec/rc/rc.d/var
new file mode 100755
index 000000000000..b4939e2bc4a0
--- /dev/null
+++ b/libexec/rc/rc.d/var
@@ -0,0 +1,114 @@
+#!/bin/sh
+#
+# Copyright (c) 1999 Matt Dillon
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: var
+# REQUIRE: mountcritlocal
+
+# NFS /var is not supported, unless NFS /var is part of diskless NFS /
+
+. /etc/rc.subr
+
+name="var"
+desc="Populate /var directory"
+stop_cmd=':'
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: mounting
+var_svcj="NO"
+
+populate_var()
+{
+ /usr/sbin/mtree -deiU -f /etc/mtree/BSD.var.dist -p /var > /dev/null
+ case ${sendmail_enable} in
+ [Nn][Oo][Nn][Ee])
+ ;;
+ *)
+ /usr/sbin/mtree -deiU -f /etc/mtree/BSD.sendmail.dist -p / > /dev/null
+ ;;
+ esac
+}
+
+# If we do not have a writable /var, create a memory filesystem for /var
+# unless told otherwise by rc.conf. We don't have /usr yet so use mkdir
+# instead of touch to test. We want mount to record its mounts so we
+# have to make sure /var/db exists before doing the mount -a.
+#
+case "${varmfs}" in
+[Yy][Ee][Ss])
+ mount_md ${varsize} /var "${varmfs_flags}"
+ ;;
+[Nn][Oo])
+ ;;
+*)
+ if /bin/mkdir -p /var/.diskless 2> /dev/null; then
+ rmdir /var/.diskless
+ else
+ mount_md ${varsize} /var "${varmfs_flags}"
+ fi
+esac
+
+
+# If we have an empty looking /var, populate it, but only if we have
+# /usr available. Hopefully, we'll eventually find a workaround, but
+# in realistic diskless setups, we're probably ok.
+case "${populate_var}" in
+[Yy][Ee][Ss])
+ populate_var
+ ;;
+[Nn][Oo])
+ exit 0
+ ;;
+*)
+ if [ -d /var/run -a -d /var/db -a -d /var/empty ] ; then
+ true
+ elif [ -x /usr/sbin/mtree ] ; then
+ populate_var
+ else
+ # We need mtree to populate /var so try mounting /usr.
+ # If this does not work, we can not boot so it is OK to
+ # try to mount out of order.
+ mount /usr
+ if [ ! -x /usr/sbin/mtree ] ; then
+ exit 1
+ else
+ populate_var
+ fi
+ fi
+ ;;
+esac
+
+# Make sure we have /var/log/utx.lastlogin and /var/log/utx.log files
+if [ ! -f /var/log/utx.lastlogin ]; then
+ cp /dev/null /var/log/utx.lastlogin
+ chmod 644 /var/log/utx.lastlogin
+fi
+if [ ! -f /var/log/utx.log ]; then
+ cp /dev/null /var/log/utx.log
+ chmod 644 /var/log/utx.log
+fi
diff --git a/libexec/rc/rc.d/var_run b/libexec/rc/rc.d/var_run
new file mode 100755
index 000000000000..9a3732f593b6
--- /dev/null
+++ b/libexec/rc/rc.d/var_run
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+# PROVIDE: var_run
+# REQUIRE: mountcritlocal
+# BEFORE: cleanvar
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name=var_run
+rcvar=var_run_enable
+extra_commands="load save"
+start_cmd="_var_run_start"
+load_cmd="_var_run_load"
+save_cmd="_var_run_save"
+stop_cmd="_var_run_stop"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+var_run_svcj="NO"
+
+_var_run_load() {
+ if [ -f "${var_run_mtree}" ] ; then
+ mtree -U -i -q -f "${var_run_mtree}" -p /var/run > /dev/null
+ fi
+}
+
+_var_run_save() {
+ if ! [ -d "${var_run_mtree%/*}" ]; then
+ mkdir -p "${var_run_mtree%/*}"
+ fi
+ mtree -dcbj -p /var/run > "${var_run_mtree}"
+}
+
+_var_run_start() {
+ if df -ttmpfs /var/run > /dev/null 2>&1; then
+ _var_run_load
+ fi
+}
+
+_var_run_stop() {
+ if checkyesno var_run_autosave; then
+ if df -ttmpfs /var/run > /dev/null 2>&1; then
+ _var_run_save
+ fi
+ fi
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/virecover b/libexec/rc/rc.d/virecover
new file mode 100755
index 000000000000..d6f9f8bdef9a
--- /dev/null
+++ b/libexec/rc/rc.d/virecover
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: virecover
+# REQUIRE: mountcritremote ldconfig
+# BEFORE: DAEMON
+#
+# XXX: should require `mail'!
+
+. /etc/rc.subr
+
+name="virecover"
+desc="Recover crashed vi sessions"
+rcvar="virecover_enable"
+stop_cmd=":"
+start_cmd="virecover_start"
+
+virecover_start()
+{
+ [ -d /var/tmp/vi.recover ] || return
+ find /var/tmp/vi.recover ! -type f -a ! -type d -delete
+ vibackup=`echo /var/tmp/vi.recover/vi.*`
+ if [ "${vibackup}" != '/var/tmp/vi.recover/vi.*' ]; then
+ echo -n 'Recovering vi editor sessions:'
+ for i in /var/tmp/vi.recover/vi.*; do
+ # Only test files that are readable.
+ if [ ! -r "${i}" ]; then
+ continue
+ fi
+
+ # Unmodified nvi editor backup files either have the
+ # execute bit set or are zero length. Delete them.
+ if [ -x "${i}" -o ! -s "${i}" ]; then
+ rm -f "${i}"
+ fi
+ done
+
+ # It is possible to get incomplete recovery files, if the editor
+ # crashes at the right time.
+ virecovery=`echo /var/tmp/vi.recover/recover.*`
+ if [ "${virecovery}" != "/var/tmp/vi.recover/recover.*" ]; then
+ for i in /var/tmp/vi.recover/recover.*; do
+ # Only test files that are readable.
+ if [ ! -r "${i}" ]; then
+ continue
+ fi
+
+ # Delete any recovery files that are zero length,
+ # corrupted, or that have no corresponding backup file.
+ # Else send mail to the user.
+ recfile=`awk '/^X-vi-recover-path:/{print $2}' < "${i}"`
+ if [ -n "${recfile}" -a -s "${recfile}" ]; then
+ sendmail -t < "${i}"
+ else
+ rm -f "${i}"
+ fi
+ done
+ fi
+ echo '.'
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+virecover_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/virtual_oss b/libexec/rc/rc.d/virtual_oss
new file mode 100644
index 000000000000..b9c830617385
--- /dev/null
+++ b/libexec/rc/rc.d/virtual_oss
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+# PROVIDE: virtual_oss
+# REQUIRE: NETWORKING kld ldconfig
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="virtual_oss"
+desc="virtual_oss device manager"
+rcvar="${name}_enable"
+
+command="/usr/sbin/${name}"
+command_args="-B"
+
+load_rc_config "$name"
+start_precmd="${name}_precmd"
+start_cmd="${name}_start"
+stop_cmd="${name}_stop"
+status_cmd="${name}_status"
+
+configs=
+pidpath="/var/run/${name}"
+virtual_oss_default_args="\
+ -S \
+ -C 2 \
+ -c 2 \
+ -r 48000 \
+ -b 24 \
+ -s 8ms \
+ -i 8 \
+ -f /dev/dsp \
+ -d dsp \
+ -t vdsp.ctl"
+
+# Set to NO by default. Set it to "YES" to enable virtual_oss.
+: "${virtual_oss_enable:="NO"}"
+
+# List of configurations to use. Default is "dsp".
+: "${virtual_oss_configs:="dsp"}"
+
+# Default (dsp) virtual_oss config.
+: "${virtual_oss_dsp:="${virtual_oss_default_args}"}"
+
+virtual_oss_pids()
+{
+ pids=$(pgrep -d ' ' ${name})
+ pids=${pids% }
+ printf '%s\n' "${pids}"
+}
+
+virtual_oss_precmd()
+{
+ /usr/bin/install -d -m 0755 -o root "${pidpath}"
+ load_kld cuse
+}
+
+start_instance()
+{
+ config="$1"
+ instance_args=$(eval "echo \$virtual_oss_${config}")
+ if [ -z "${instance_args}" ]; then
+ warn "no such config: ${config}"
+ else
+ startmsg -n "Starting virtual_oss config: ${config}: "
+ ${command} \
+ ${command_args} \
+ -D "${pidpath}/${config}.pid" \
+ ${instance_args}
+ startmsg "done"
+ fi
+}
+
+stop_instance()
+{
+ config="$1"
+ instance_args=$(eval "echo \$virtual_oss_${config}")
+ if [ -z "${instance_args}" ]; then
+ warn "no such config: ${config}"
+ else
+ startmsg -n "Stopping virtual_oss config: ${config}: "
+ kill "$(cat "${pidpath}/${config}.pid")"
+ rm -f "${pidpath}/${config}.pid"
+ startmsg "done"
+ fi
+}
+
+virtual_oss_start()
+{
+ configs="$1"
+ [ -z "${configs}" ] && configs="${virtual_oss_configs}"
+ for config in ${configs}; do
+ start_instance "${config}"
+ done
+}
+
+virtual_oss_stop()
+{
+ configs="$1"
+ [ -z "${configs}" ] && configs="${virtual_oss_configs}"
+ for config in ${configs}; do
+ stop_instance "${config}"
+ done
+}
+
+virtual_oss_status()
+{
+ pids=$(virtual_oss_pids)
+
+ if [ "${pids}" ]; then
+ echo "${name} is running as pid ${pids}."
+ else
+ echo "${name} is not running."
+ return 1
+ fi
+}
+
+run_rc_command "$@"
diff --git a/libexec/rc/rc.d/watchdogd b/libexec/rc/rc.d/watchdogd
new file mode 100755
index 000000000000..6cd37b8c5ceb
--- /dev/null
+++ b/libexec/rc/rc.d/watchdogd
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+# Copyright (c) 2003 Sean M. Kelly <smkelly@FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# PROVIDE: watchdogd
+# REQUIRE: FILESYSTEMS syslogd
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="watchdogd"
+desc="Watchdog daemon"
+rcvar="watchdogd_enable"
+command="/usr/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+start_precmd="watchdogd_prestart"
+stop_precmd="watchdogd_prestop"
+stop_postcmd="watchdogd_poststop"
+watchdog_command="/usr/sbin/watchdog"
+
+watchdogd_prestart()
+{
+ if [ -n "${watchdogd_timeout}" ] ; then
+ rc_flags="${rc_flags} -t ${watchdogd_timeout}"
+ fi
+ if [ -n "$watchdogd_shutdown_timeout" ] ; then
+ rc_flags="${rc_flags} -x ${watchdogd_shutdown_timeout}"
+ fi
+ return 0
+}
+
+watchdogd_prestop()
+{
+ sig_stop="${watchdogd_sig_stop:-TERM}"
+}
+
+watchdogd_poststop()
+{
+ if [ ${watchdogd_shutdown_timeout:-0} -gt 0 ] ; then
+ case "${rc_shutdown}" in
+ "reboot")
+ info "watchdog timer is set to" \
+ ${watchdogd_shutdown_timeout} "before shutdown"
+ return 0
+ ;;
+ "single")
+ info "watchdog timer is disabled before going to" \
+ "single user mode"
+ ${watchdog_command} -t 0
+ ;;
+ "")
+ info "watchdog timer is disabled after administrative" \
+ "${name} stop"
+ ${watchdog_command} -t 0
+ ;;
+ *)
+ warn "unknown shutdown mode '${rc_shutdown}'"
+ warn "watchdog timer is set to ${watchdogd_shutdown_timeout}"
+ return 0
+ ;;
+ esac
+ fi
+ return 0
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: privileged operations
+watchdogd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/wpa_supplicant b/libexec/rc/rc.d/wpa_supplicant
new file mode 100755
index 000000000000..e11dddfb5fd3
--- /dev/null
+++ b/libexec/rc/rc.d/wpa_supplicant
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: wpa_supplicant
+# REQUIRE: mountcritremote
+# KEYWORD: nojail nostart
+
+. /etc/rc.subr
+. /etc/network.subr
+
+name="wpa_supplicant"
+desc="WPA/802.11i Supplicant for wireless network devices"
+rcvar=
+
+ifn="$2"
+if [ -z "$ifn" ]; then
+ return 1
+fi
+
+if is_wired_interface ${ifn} ; then
+ driver="wired"
+else
+ driver="bsd"
+fi
+
+load_rc_config $name
+
+command=${wpa_supplicant_program}
+conf_file=${wpa_supplicant_conf_file}
+pidfile="/var/run/${name}/${ifn}.pid"
+command_args="-B -i $ifn -c $conf_file -D $driver -P $pidfile"
+required_files=$conf_file
+required_modules="wlan_wep wlan_tkip wlan_ccmp wlan_gcmp"
+
+# doesn't make sense to run in a svcj: nojail keyword
+wpa_supplicant_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ypbind b/libexec/rc/rc.d/ypbind
new file mode 100755
index 000000000000..a6bf00f1ed9d
--- /dev/null
+++ b/libexec/rc/rc.d/ypbind
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ypbind
+# REQUIRE: ypserv
+# BEFORE: DAEMON
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="ypbind"
+desc="NIS domain binding daemon"
+rcvar="nis_client_enable"
+
+: ${ypbind_svcj_options:="net_basic"}
+
+load_rc_config $name
+
+command="/usr/sbin/${name}"
+command_args="${nis_client_flags}"
+
+start_precmd="ypbind_precmd"
+
+ypbind_precmd()
+{
+ local _domain
+
+ force_depend rpcbind || return 1
+
+ _domain=`domainname`
+ if [ -z "$_domain" ]; then
+ warn "NIS domainname(1) is not set."
+ return 1
+ fi
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ypldap b/libexec/rc/rc.d/ypldap
new file mode 100755
index 000000000000..579b004a07c0
--- /dev/null
+++ b/libexec/rc/rc.d/ypldap
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ypldap
+# REQUIRE: ypserv
+# BEFORE: DAEMON
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="ypldap"
+rcvar="nis_ypldap_enable"
+
+: ${ypldap_svcj_options:="net_basic"}
+
+load_rc_config $name
+
+command="/usr/sbin/${name}"
+command_args="${nis_ypldap_flags}"
+
+start_precmd="ypldap_precmd"
+
+ypldap_precmd()
+{
+ force_depend ypserv nis_server || return 1
+}
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/yppasswdd b/libexec/rc/rc.d/yppasswdd
new file mode 100755
index 000000000000..81a04d753305
--- /dev/null
+++ b/libexec/rc/rc.d/yppasswdd
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: yppasswdd
+# REQUIRE: ypserv ypset
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="yppasswdd"
+desc="Server for updating NIS passwords"
+rcvar="nis_yppasswdd_enable"
+
+: ${yppasswdd_svcj_options:="net_basic"}
+
+load_rc_config $name
+
+command="/usr/sbin/rpc.${name}"
+command_args="${nis_yppasswdd_flags}"
+
+start_precmd="yppasswdd_precmd"
+
+yppasswdd_precmd()
+{
+ local _domain
+
+ force_depend rpcbind || return 1
+ force_depend ypserv nis_server || return 1
+
+ _domain=`domainname`
+ if [ -z "$_domain" ]; then
+ warn "NIS domainname(1) is not set."
+ return 1
+ fi
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ypserv b/libexec/rc/rc.d/ypserv
new file mode 100755
index 000000000000..8cae179fdd11
--- /dev/null
+++ b/libexec/rc/rc.d/ypserv
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ypserv
+# REQUIRE: rpcbind
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="ypserv"
+desc="NIS database server"
+rcvar="nis_server_enable"
+
+: ${ypserv_svcj_options:="net_basic"}
+
+load_rc_config $name
+
+command="/usr/sbin/${name}"
+command_args="${nis_server_flags}"
+
+start_precmd="ypserv_prestart"
+
+ypserv_prestart()
+{
+ local _domain
+
+ force_depend rpcbind || return 1
+
+ _domain=`domainname`
+ if [ -z "$_domain" ]; then
+ warn "NIS domainname(1) is not set."
+ return 1
+ fi
+ if [ ! -d /var/yp/$_domain/. ]; then
+ warn "/var/yp/$_domain is not a directory."
+ return 1
+ fi
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ypset b/libexec/rc/rc.d/ypset
new file mode 100755
index 000000000000..123a94ea44e8
--- /dev/null
+++ b/libexec/rc/rc.d/ypset
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ypset
+# REQUIRE: ypbind
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="ypset"
+desc="tell ypbind(8) which YP server process to use"
+rcvar="nis_ypset_enable"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+ypset_svcj="NO"
+
+command="/usr/sbin/${name}"
+command_args="${nis_ypset_flags}"
+
+start_precmd="ypset_precmd"
+
+ypset_precmd()
+{
+ local _domain
+
+ force_depend rpcbind || return 1
+ force_depend ypbind nis_client || return 1
+
+ _domain=`domainname`
+ if [ -z "$_domain" ]; then
+ warn "NIS domainname(1) is not set."
+ return 1
+ fi
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ypupdated b/libexec/rc/rc.d/ypupdated
new file mode 100755
index 000000000000..1a4c595c745a
--- /dev/null
+++ b/libexec/rc/rc.d/ypupdated
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ypupdated
+# REQUIRE: rpcbind ypserv
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="ypupdated"
+rcvar="rpc_ypupdated_enable"
+
+: ${ypupdated_svcj_options:="net_basic"}
+
+load_rc_config $name
+
+command="/usr/sbin/rpc.${name}"
+start_precmd="rpc_ypupdated_precmd"
+
+rpc_ypupdated_precmd()
+{
+ local _domain
+
+ force_depend rpcbind || return 1
+ force_depend ypserv nis_server || return 1
+
+ _domain=`domainname`
+ if [ -z "$_domain" ]; then
+ warn "NIS domainname(1) is not set."
+ return 1
+ fi
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/ypxfrd b/libexec/rc/rc.d/ypxfrd
new file mode 100755
index 000000000000..ea929b0d25ce
--- /dev/null
+++ b/libexec/rc/rc.d/ypxfrd
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: ypxfrd
+# REQUIRE: rpcbind ypserv
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="ypxfrd"
+desc="NIS map transfer server"
+rcvar="nis_ypxfrd_enable"
+
+: ${ypxfrd_svcj_options:="net_basic"}
+
+load_rc_config $name
+
+command="/usr/sbin/rpc.${name}"
+command_args="${nis_ypxfrd_flags}"
+
+start_precmd="ypxfrd_precmd"
+
+ypxfrd_precmd()
+{
+ local _domain
+
+ force_depend rpcbind || return 1
+ force_depend ypserv nis_server || return 1
+
+ _domain=`domainname`
+ if [ -z "$_domain" ]; then
+ warn "NIS domainname(1) is not set."
+ return 1
+ fi
+}
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/zfs b/libexec/rc/rc.d/zfs
new file mode 100755
index 000000000000..f88f65c2ec18
--- /dev/null
+++ b/libexec/rc/rc.d/zfs
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: zfs
+# REQUIRE: zfsbe
+# BEFORE: FILESYSTEMS var
+
+. /etc/rc.subr
+
+name="zfs"
+desc="Mount and share ZFS datasets"
+rcvar="zfs_enable"
+start_cmd="zfs_start"
+start_postcmd="zfs_poststart"
+stop_cmd="zfs_stop"
+required_modules="zfs"
+
+zfs_start_jail()
+{
+ if check_jail mount_allowed; then
+ zfs mount -a
+ fi
+}
+
+zfs_start_main()
+{
+ zfs mount -va
+ zfs share -a
+ if [ ! -r /etc/zfs/exports ]; then
+ touch /etc/zfs/exports
+ fi
+}
+
+zfs_start()
+{
+ if check_jail jailed; then
+ zfs_start_jail
+ else
+ zfs_start_main
+ fi
+}
+
+zfs_poststart()
+{
+ # Some of the keys to decrypt datasets are potentially stored on ZFS
+ # datasets that just got mounted. Let's try to load those keys and
+ # mount the datasets.
+ if checkyesno zfskeys_enable; then
+ /etc/rc.d/zfskeys start
+ zfs_start
+ fi
+}
+
+zfs_stop_jail()
+{
+ if check_jail mount_allowed; then
+ zfs unmount -a
+ fi
+}
+
+zfs_stop_main()
+{
+ zfs unshare -a
+ zfs unmount -a
+}
+
+zfs_stop()
+{
+ if check_jail jailed; then
+ zfs_stop_jail
+ else
+ zfs_stop_main
+ fi
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: mounting / config setting
+zfs_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/zfsbe b/libexec/rc/rc.d/zfsbe
new file mode 100755
index 000000000000..22d53f219679
--- /dev/null
+++ b/libexec/rc/rc.d/zfsbe
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: zfsbe
+# REQUIRE: mountcritlocal
+
+# Handle boot environment subordinate filesystems
+# that may have canmount property set to noauto.
+# For these filesystems mountpoint relative to /
+# must be the same as their dataset name relative
+# to BE root dataset.
+
+. /etc/rc.subr
+
+name="zfsbe"
+rcvar="zfs_enable"
+start_cmd="be_start"
+stop_cmd="be_stop"
+required_modules="zfs"
+
+mount_subordinate()
+{
+ local _be
+
+ _be=$1
+ zfs list -rH -o mountpoint,name,canmount,mounted -s mountpoint -t filesystem $_be | \
+ while read _mp _name _canmount _mounted ; do
+ # skip filesystems that must not be mounted
+ [ "$_canmount" = "off" ] && continue
+ # skip filesystems that are already mounted
+ [ "$_mounted" = "yes" ] && continue
+ case "$_mp" in
+ "none" | "legacy" | "/" | "/$_be")
+ # do nothing for filesystems with unset or legacy mountpoint
+ # or those that would be mounted over /
+ ;;
+ "/$_be/"*)
+ # filesystems with mountpoint relative to BE
+ mount -t zfs $_name ${_mp#/$_be}
+ ;;
+ *)
+ # filesystems with mountpoint elsewhere
+ zfs mount $_name
+ ;;
+ esac
+ done
+}
+
+activate_bootonce()
+{
+ local _dev
+ local _bootonce
+ local _be
+
+ _dev=$1
+ _be=${_dev##*/}
+
+ _bootonce=$(kenv -q zfs-bootonce)
+ if [ "$_bootonce" = "zfs:${_dev}:" ] ; then
+ bectl activate $_be
+ fi
+}
+
+be_start()
+{
+ if check_jail jailed; then
+ :
+ else
+ mount -p | while read _dev _mp _type _rest; do
+ [ $_mp = "/" ] || continue
+ if [ $_type = "zfs" ] ; then
+ mount_subordinate $_dev
+ if checkyesno zfs_bootonce_activate; then
+ activate_bootonce $_dev
+ fi
+ fi
+ break
+ done
+ fi
+}
+
+be_stop()
+{
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: mounting / config setting
+zfsbe_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/zfsd b/libexec/rc/rc.d/zfsd
new file mode 100755
index 000000000000..f0abeeeb446b
--- /dev/null
+++ b/libexec/rc/rc.d/zfsd
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: zfsd
+# REQUIRE: devd zfs
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="zfsd"
+rcvar="zfsd_enable"
+command="/usr/sbin/${name}"
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+zfsd_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/zfskeys b/libexec/rc/rc.d/zfskeys
new file mode 100755
index 000000000000..aff0224d5c9d
--- /dev/null
+++ b/libexec/rc/rc.d/zfskeys
@@ -0,0 +1,131 @@
+#!/bin/sh
+
+# PROVIDE: zfskeys
+# REQUIRE: zpool
+# BEFORE: zfs zvol
+
+. /etc/rc.subr
+
+name="zfskeys"
+desc="Load dataset keys"
+rcvar="zfskeys_enable"
+extra_commands="status"
+start_cmd="load_zfs_keys"
+stop_cmd="unload_zfs_keys"
+status_cmd="status_zfs_keys"
+required_modules="zfs"
+
+# Note that zfskeys_datasets must have any character found in IFS escaped.
+# Forcibly unmounting/unloading only applies to filesystems; ignored for zvols.
+: ${zfskeys_datasets:=''}
+: ${zfskeys_timeout:=10}
+: ${zfskeys_unload_force:='NO'}
+
+encode_args()
+{
+ shift && [ $# -gt 0 ] && printf "%s\0" "$@" | b64encode -r -
+}
+
+list_datasets()
+{
+ if [ "$zfskeys_args" ]; then
+ echo "$zfskeys_args" | b64decode -r |
+ xargs -0 zfs get -H -s local -o value,name keylocation
+ elif [ ! "$zfskeys_datasets" ]; then
+ zfs get -H -t filesystem,volume -s local -o value,name keylocation
+ else
+ echo "$zfskeys_datasets" | xargs -n 1 zfs get -H -s local \
+ -o value,name keylocation
+ fi
+}
+
+unlock_fs()
+{
+ local fs="$1"
+ local kl="$2"
+ local k="${kl##file://}"
+
+ if [ "$kl" == "prompt" ]
+ then
+ echo "Key prompt for $fs."
+ if zfs load-key -L "$kl" "$fs" < /dev/tty > /dev/tty 2>/dev/tty ; then
+ echo "Key loaded for $fs."
+ else
+ echo "Key failed to load for $fs."
+ fi
+ elif [ "$k" ] && [ -f "$k" ] && [ -s "$k" ] && [ -r "$k" ]; then
+ if [ "$(zfs get -Ho value keystatus "$fs")" = 'available' ]; then
+ echo "Key already loaded for $fs."
+ elif keytest=$(zfs load-key -n -L "$kl" "$fs" 2>&1); then
+ echo "Loading key for $fs from $kl.."
+ if ! keyload=$(timeout $zfskeys_timeout zfs load-key -L "$kl" "$fs" 2>&1) ; then
+ if [ $? -eq 124 ]; then
+ echo "Timed out loading key from $kl for $fs"
+ else
+ echo "Failed to load key from $kl for $fs:"
+ echo "$keyload"
+ fi
+ fi
+ else
+ echo "Could not verify key from $kl for $fs:"
+ echo "$keytest"
+ fi
+ else
+ echo "Key file $k not found, empty or unreadable. Skipping $fs.."
+ fi
+}
+
+lock_fs()
+{
+ local fs=$1
+
+ if [ "$(zfs get -Ho value mounted "$fs")" = 'yes' ]; then
+ if checkyesno zfskeys_unload_force ; then
+ zfs unmount -f "$fs" && echo "Forcibly unmounted $fs."
+ else
+ zfs unmount "$fs" && echo "Unmounted $fs."
+ fi
+ fi
+ if [ "$?" -ne 0 ]; then
+ echo "Unmount failed for $fs"
+ elif [ "$(zfs get -Ho value keystatus "$fs")" = 'available' ]; then
+ zfs unload-key "$fs" && echo "Unloaded key for $fs."
+ else
+ echo "No key loaded for $fs."
+ fi
+}
+
+status_zfs_keys()
+{
+ local IFS=$(printf "\t")
+
+ list_datasets | while read kl fs ; do
+ echo "$fs: $(zfs get -Ho value keystatus "$fs")"
+ done
+}
+
+load_zfs_keys()
+{
+ local IFS=$(printf "\t")
+
+ list_datasets | while read kl fs ; do
+ unlock_fs "$fs" "$kl"
+ done
+}
+
+unload_zfs_keys()
+{
+ local IFS=$(printf "\t")
+
+ list_datasets | while read kl fs ; do
+ lock_fs "$fs"
+ done
+}
+
+zfskeys_args=$(encode_args "$@")
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+zfskeys_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/zpool b/libexec/rc/rc.d/zpool
new file mode 100755
index 000000000000..63f040ad122b
--- /dev/null
+++ b/libexec/rc/rc.d/zpool
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: zpool
+# REQUIRE: hostid disks mountcritlocal
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="zpool"
+desc="Import ZPOOLs"
+rcvar="zfs_enable"
+start_cmd="zpool_start"
+required_modules="zfs"
+
+zpool_start()
+{
+ local cachefile
+
+ for cachefile in /etc/zfs/zpool.cache /boot/zfs/zpool.cache; do
+ if [ -r $cachefile ]; then
+ zpool import -c $cachefile -a -N
+ if [ $? -ne 0 ]; then
+ echo "Import of zpool cache ${cachefile} failed," \
+ "will retry after root mount hold release"
+ root_hold_wait
+ zpool import -c $cachefile -a -N
+ fi
+ break
+ fi
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj
+zpool_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/zpoolreguid b/libexec/rc/rc.d/zpoolreguid
new file mode 100755
index 000000000000..c19f52d3d702
--- /dev/null
+++ b/libexec/rc/rc.d/zpoolreguid
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# PROVIDE: zpoolreguid
+# REQUIRE: zpool
+# BEFORE: FILESYSTEMS
+# KEYWORD: firstboot nojail
+
+. /etc/rc.subr
+
+name="zpoolreguid"
+desc="Generate a new zpool GUID"
+rcvar="zfs_enable"
+start_cmd="zpoolreguid_start"
+
+zpoolreguid_start()
+{
+ local pool
+
+ for pool in ${zpool_reguid}; do
+ zpool reguid $pool
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+zpoolreguid_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/zpoolupgrade b/libexec/rc/rc.d/zpoolupgrade
new file mode 100755
index 000000000000..5e623a9c2bf0
--- /dev/null
+++ b/libexec/rc/rc.d/zpoolupgrade
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# PROVIDE: zpoolupgrade
+# REQUIRE: zpool
+# BEFORE: FILESYSTEMS
+# KEYWORD: firstboot nojail
+
+. /etc/rc.subr
+
+name="zpoolupgrade"
+desc="Upgrade zpool version"
+rcvar="zfs_enable"
+start_cmd="zpoolupgrade_start"
+
+zpoolupgrade_start()
+{
+ local pool
+
+ for pool in ${zpool_upgrade}; do
+ zpool upgrade $pool
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+zpoolupgrade_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.d/zvol b/libexec/rc/rc.d/zvol
new file mode 100755
index 000000000000..b9f17fad5bfd
--- /dev/null
+++ b/libexec/rc/rc.d/zvol
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+#
+
+# PROVIDE: zvol
+# REQUIRE: zpool
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="zvol"
+desc="Activate swap on ZVOLs"
+rcvar="zfs_enable"
+start_cmd="zvol_start"
+stop_cmd="zvol_stop"
+required_modules="zfs"
+
+zvol_start()
+{
+ # Enable swap on ZVOLs with property org.freebsd:swap=on.
+ zfs list -H -o org.freebsd:swap,name -t volume |
+ while read state name; do
+ case "${state}" in
+ ([oO][nN])
+ swapon /dev/zvol/${name}
+ ;;
+ esac
+ done
+}
+
+zvol_stop()
+{
+ # Disable swap on ZVOLs with property org.freebsd:swap=on.
+ zfs list -H -o org.freebsd:swap,name -t volume |
+ while read state name; do
+ case "${state}" in
+ ([oO][nN])
+ swapoff /dev/zvol/${name}
+ ;;
+ esac
+ done
+}
+
+load_rc_config $name
+
+# doesn't make sense to run in a svcj: config setting
+zvol_svcj="NO"
+
+run_rc_command "$1"
diff --git a/libexec/rc/rc.firewall b/libexec/rc/rc.firewall
new file mode 100644
index 000000000000..e4fc8cc3db78
--- /dev/null
+++ b/libexec/rc/rc.firewall
@@ -0,0 +1,553 @@
+#!/bin/sh -
+# Copyright (c) 1996 Poul-Henning Kamp
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+#
+# Setup system for ipfw(4) firewall service.
+#
+
+# Suck in the configuration variables.
+if [ -z "${source_rc_confs_defined}" ]; then
+ if [ -r /etc/defaults/rc.conf ]; then
+ . /etc/defaults/rc.conf
+ source_rc_confs
+ elif [ -r /etc/rc.conf ]; then
+ . /etc/rc.conf
+ fi
+fi
+
+############
+# Define the firewall type in /etc/rc.conf. Valid values are:
+# open - will allow anyone in
+# client - will try to protect just this machine
+# simple - will try to protect a whole network
+# closed - totally disables IP services except via lo0 interface
+# workstation - will try to protect just this machine using stateful
+# firewalling. See below for rc.conf variables used
+# UNKNOWN - disables the loading of firewall rules.
+# filename - will load the rules in the given filename (full path required)
+#
+# For ``client'' and ``simple'' the entries below should be customized
+# appropriately.
+
+############
+#
+# If you don't know enough about packet filtering, we suggest that you
+# take time to read this book:
+#
+# Building Internet Firewalls, 2nd Edition
+# Brent Chapman and Elizabeth Zwicky
+#
+# O'Reilly & Associates, Inc
+# ISBN 1-56592-871-7
+# http://www.ora.com/
+# http://www.oreilly.com/catalog/fire2/
+#
+# For a more advanced treatment of Internet Security read:
+#
+# Firewalls and Internet Security: Repelling the Wily Hacker, 2nd Edition
+# William R. Cheswick, Steven M. Bellowin, Aviel D. Rubin
+#
+# Addison-Wesley / Prentice Hall
+# ISBN 0-201-63466-X
+# http://www.pearsonhighered.com/
+# http://www.pearsonhighered.com/educator/academic/product/0,3110,020163466X,00.html
+#
+
+setup_loopback() {
+ ############
+ # Only in rare cases do you want to change these rules
+ #
+ ${fwcmd} add 100 pass all from any to any via lo0
+ ${fwcmd} add 200 deny all from any to 127.0.0.0/8
+ ${fwcmd} add 300 deny ip from 127.0.0.0/8 to any
+ if [ $ipv6_available -eq 0 ]; then
+ ${fwcmd} add 400 deny all from any to ::1
+ ${fwcmd} add 500 deny all from ::1 to any
+ fi
+}
+
+setup_ipv6_mandatory() {
+ [ $ipv6_available -eq 0 ] || return 0
+
+ ############
+ # Only in rare cases do you want to change these rules
+ #
+ # ND
+ #
+ # DAD
+ ${fwcmd} add pass ipv6-icmp from :: to ff02::/16
+ # RS, RA, NS, NA, redirect...
+ ${fwcmd} add pass ipv6-icmp from fe80::/10 to fe80::/10
+ ${fwcmd} add pass ipv6-icmp from fe80::/10 to ff02::/16
+
+ # Allow ICMPv6 destination unreachable
+ ${fwcmd} add pass ipv6-icmp from any to any icmp6types 1
+
+ # Allow NS/NA/toobig (don't filter it out)
+ ${fwcmd} add pass ipv6-icmp from any to any icmp6types 2,135,136
+}
+
+. /etc/rc.subr
+. /etc/network.subr
+
+if [ -n "${1}" ]; then
+ firewall_type="${1}"
+fi
+if [ -z "${firewall_rc_config_load}" ]; then
+ load_rc_config ipfw
+else
+ for i in ${firewall_rc_config_load}; do
+ load_rc_config $i
+ done
+fi
+
+afexists inet6
+ipv6_available=$?
+
+############
+# Set quiet mode if requested
+#
+case ${firewall_quiet} in
+[Yy][Ee][Ss])
+ fwcmd="/sbin/ipfw -q"
+ ;;
+*)
+ fwcmd="/sbin/ipfw"
+ ;;
+esac
+
+############
+# Flush out the list before we begin.
+#
+${fwcmd} -f flush
+
+setup_loopback
+setup_ipv6_mandatory
+
+############
+# Network Address Translation. All packets are passed to natd(8)
+# before they encounter your remaining rules. The firewall rules
+# will then be run again on each packet after translation by natd
+# starting at the rule number following the divert rule.
+#
+# For ``simple'' firewall type the divert rule should be put to a
+# different place to not interfere with address-checking rules.
+#
+case ${firewall_type} in
+[Oo][Pp][Ee][Nn]|[Cc][Ll][Ii][Ee][Nn][Tt])
+ case ${natd_enable} in
+ [Yy][Ee][Ss])
+ if [ -n "${natd_interface}" ]; then
+ ${fwcmd} add 50 divert natd ip4 from any to any via ${natd_interface}
+ fi
+ ;;
+ esac
+ case ${firewall_nat_enable} in
+ [Yy][Ee][Ss])
+ if [ -n "${firewall_nat_interface}" ]; then
+ if echo "${firewall_nat_interface}" | \
+ grep -q -E '^[0-9]+(\.[0-9]+){0,3}$'; then
+ firewall_nat_flags="ip ${firewall_nat_interface} ${firewall_nat_flags}"
+ else
+ firewall_nat_flags="if ${firewall_nat_interface} ${firewall_nat_flags}"
+ fi
+ ${fwcmd} nat 123 config log ${firewall_nat_flags}
+ ${fwcmd} add 50 nat 123 ip4 from any to any via ${firewall_nat_interface}
+ fi
+ ;;
+ esac
+esac
+
+############
+# If you just configured ipfw in the kernel as a tool to solve network
+# problems or you just want to disallow some particular kinds of traffic
+# then you will want to change the default policy to open. You can also
+# do this as your only action by setting the firewall_type to ``open''.
+#
+# ${fwcmd} add 65000 pass all from any to any
+
+
+# Prototype setups.
+#
+case ${firewall_type} in
+[Oo][Pp][Ee][Nn])
+ ${fwcmd} add 65000 pass all from any to any
+ ;;
+
+[Cc][Ll][Ii][Ee][Nn][Tt])
+ ############
+ # This is a prototype setup that will protect your system somewhat
+ # against people from outside your own network.
+ #
+ # Configuration:
+ # firewall_client_net: Network address of local IPv4 network.
+ # firewall_client_net_ipv6: Network address of local IPv6 network.
+ ############
+
+ # set this to your local network
+ net="$firewall_client_net"
+ net6="$firewall_client_net_ipv6"
+
+ # Allow limited broadcast traffic from my own net.
+ ${fwcmd} add pass all from ${net} to 255.255.255.255
+
+ # Allow any traffic to or from my own net.
+ ${fwcmd} add pass all from me to ${net}
+ ${fwcmd} add pass all from ${net} to me
+ if [ -n "$net6" ]; then
+ ${fwcmd} add pass all from me to ${net6}
+ ${fwcmd} add pass all from ${net6} to me
+ # Allow any link-local multicast traffic
+ ${fwcmd} add pass all from fe80::/10 to ff02::/16
+ ${fwcmd} add pass all from ${net6} to ff02::/16
+ # Allow DHCPv6
+ ${fwcmd} add pass udp from fe80::/10 to me 546
+ fi
+
+ # Allow TCP through if setup succeeded
+ ${fwcmd} add pass tcp from any to any established
+
+ # Allow IP fragments to pass through
+ ${fwcmd} add pass all from any to any frag
+
+ # Allow setup of incoming email
+ ${fwcmd} add pass tcp from any to me 25 setup
+
+ # Allow setup of outgoing TCP connections only
+ ${fwcmd} add pass tcp from me to any setup
+
+ # Disallow setup of all other TCP connections
+ ${fwcmd} add deny tcp from any to any setup
+
+ # Allow DNS queries out in the world
+ ${fwcmd} add pass udp from me to any 53 keep-state
+
+ # Allow NTP queries out in the world
+ ${fwcmd} add pass udp from me to any 123 keep-state
+
+ # Everything else is denied by default, unless the
+ # IPFIREWALL_DEFAULT_TO_ACCEPT option is set in your kernel
+ # config file.
+ ;;
+
+[Ss][Ii][Mm][Pp][Ll][Ee])
+ ############
+ # This is a prototype setup for a simple firewall. Configure this
+ # machine as a DNS and NTP server, and point all the machines
+ # on the inside at this machine for those services.
+ #
+ # Configuration:
+ # firewall_simple_iif: Inside IPv4 network interface.
+ # firewall_simple_inet: Inside IPv4 network address.
+ # firewall_simple_oif: Outside IPv4 network interface.
+ # firewall_simple_onet: Outside IPv4 network address.
+ # firewall_simple_iif_ipv6: Inside IPv6 network interface.
+ # firewall_simple_inet_ipv6: Inside IPv6 network prefix.
+ # firewall_simple_oif_ipv6: Outside IPv6 network interface.
+ # firewall_simple_onet_ipv6: Outside IPv6 network prefix.
+ ############
+ BAD_ADDR_TBL=13
+
+ # set these to your outside interface network
+ oif="$firewall_simple_oif"
+ onet="$firewall_simple_onet"
+ oif6="${firewall_simple_oif_ipv6:-$firewall_simple_oif}"
+ onet6="$firewall_simple_onet_ipv6"
+
+ # set these to your inside interface network
+ iif="$firewall_simple_iif"
+ inet="$firewall_simple_inet"
+ iif6="${firewall_simple_iif_ipv6:-$firewall_simple_iif}"
+ inet6="$firewall_simple_inet_ipv6"
+
+ # Stop spoofing
+ ${fwcmd} add deny all from ${inet} to any in via ${oif}
+ ${fwcmd} add deny all from ${onet} to any in via ${iif}
+ if [ -n "$inet6" ]; then
+ ${fwcmd} add deny all from ${inet6} to any in via ${oif6}
+ if [ -n "$onet6" ]; then
+ ${fwcmd} add deny all from ${onet6} to any in \
+ via ${iif6}
+ fi
+ fi
+
+ # Define stuff we should never send out or receive in.
+ # Stop RFC1918 nets on the outside interface
+ ${fwcmd} table ${BAD_ADDR_TBL} flush
+ ${fwcmd} table ${BAD_ADDR_TBL} add 10.0.0.0/8
+ ${fwcmd} table ${BAD_ADDR_TBL} add 172.16.0.0/12
+ ${fwcmd} table ${BAD_ADDR_TBL} add 192.168.0.0/16
+
+ # And stop draft-manning-dsua-03.txt (1 May 2000) nets (includes RESERVED-1,
+ # DHCP auto-configuration, NET-TEST, MULTICAST (class D), and class E)
+ # on the outside interface
+ ${fwcmd} table ${BAD_ADDR_TBL} add 0.0.0.0/8
+ ${fwcmd} table ${BAD_ADDR_TBL} add 169.254.0.0/16
+ ${fwcmd} table ${BAD_ADDR_TBL} add 192.0.2.0/24
+ ${fwcmd} table ${BAD_ADDR_TBL} add 224.0.0.0/4
+ ${fwcmd} table ${BAD_ADDR_TBL} add 240.0.0.0/4
+
+ ${fwcmd} add deny all from any to "table($BAD_ADDR_TBL)" via ${oif}
+
+ # Network Address Translation. This rule is placed here deliberately
+ # so that it does not interfere with the surrounding address-checking
+ # rules. If for example one of your internal LAN machines had its IP
+ # address set to 192.0.2.1 then an incoming packet for it after being
+ # translated by natd(8) would match the `deny' rule above. Similarly
+ # an outgoing packet originated from it before being translated would
+ # match the `deny' rule below.
+ case ${natd_enable} in
+ [Yy][Ee][Ss])
+ if [ -n "${natd_interface}" ]; then
+ ${fwcmd} add divert natd ip4 from any to any via ${natd_interface}
+ fi
+ ;;
+ esac
+
+ ${fwcmd} add deny all from "table($BAD_ADDR_TBL)" to any via ${oif}
+ if [ -n "$inet6" ]; then
+ # Stop unique local unicast address on the outside interface
+ ${fwcmd} add deny all from fc00::/7 to any via ${oif6}
+ ${fwcmd} add deny all from any to fc00::/7 via ${oif6}
+
+ # Stop site-local on the outside interface
+ ${fwcmd} add deny all from fec0::/10 to any via ${oif6}
+ ${fwcmd} add deny all from any to fec0::/10 via ${oif6}
+
+ # Disallow "internal" addresses to appear on the wire.
+ ${fwcmd} add deny all from ::ffff:0.0.0.0/96 to any \
+ via ${oif6}
+ ${fwcmd} add deny all from any to ::ffff:0.0.0.0/96 \
+ via ${oif6}
+
+ # Disallow packets to malicious IPv4 compatible prefix.
+ ${fwcmd} add deny all from ::224.0.0.0/100 to any via ${oif6}
+ ${fwcmd} add deny all from any to ::224.0.0.0/100 via ${oif6}
+ ${fwcmd} add deny all from ::127.0.0.0/104 to any via ${oif6}
+ ${fwcmd} add deny all from any to ::127.0.0.0/104 via ${oif6}
+ ${fwcmd} add deny all from ::0.0.0.0/104 to any via ${oif6}
+ ${fwcmd} add deny all from any to ::0.0.0.0/104 via ${oif6}
+ ${fwcmd} add deny all from ::255.0.0.0/104 to any via ${oif6}
+ ${fwcmd} add deny all from any to ::255.0.0.0/104 via ${oif6}
+
+ ${fwcmd} add deny all from ::0.0.0.0/96 to any via ${oif6}
+ ${fwcmd} add deny all from any to ::0.0.0.0/96 via ${oif6}
+
+ # Disallow packets to malicious 6to4 prefix.
+ ${fwcmd} add deny all from 2002:e000::/20 to any via ${oif6}
+ ${fwcmd} add deny all from any to 2002:e000::/20 via ${oif6}
+ ${fwcmd} add deny all from 2002:7f00::/24 to any via ${oif6}
+ ${fwcmd} add deny all from any to 2002:7f00::/24 via ${oif6}
+ ${fwcmd} add deny all from 2002:0000::/24 to any via ${oif6}
+ ${fwcmd} add deny all from any to 2002:0000::/24 via ${oif6}
+ ${fwcmd} add deny all from 2002:ff00::/24 to any via ${oif6}
+ ${fwcmd} add deny all from any to 2002:ff00::/24 via ${oif6}
+
+ ${fwcmd} add deny all from 2002:0a00::/24 to any via ${oif6}
+ ${fwcmd} add deny all from any to 2002:0a00::/24 via ${oif6}
+ ${fwcmd} add deny all from 2002:ac10::/28 to any via ${oif6}
+ ${fwcmd} add deny all from any to 2002:ac10::/28 via ${oif6}
+ ${fwcmd} add deny all from 2002:c0a8::/32 to any via ${oif6}
+ ${fwcmd} add deny all from any to 2002:c0a8::/32 via ${oif6}
+
+ ${fwcmd} add deny all from ff05::/16 to any via ${oif6}
+ ${fwcmd} add deny all from any to ff05::/16 via ${oif6}
+ fi
+
+ # Allow TCP through if setup succeeded
+ ${fwcmd} add pass tcp from any to any established
+
+ # Allow IP fragments to pass through
+ ${fwcmd} add pass all from any to any frag
+
+ # Allow setup of incoming email
+ ${fwcmd} add pass tcp from any to me 25 setup
+
+ # Allow access to our DNS
+ ${fwcmd} add pass tcp from any to me 53 setup
+ ${fwcmd} add pass udp from any to me 53
+ ${fwcmd} add pass udp from me 53 to any
+
+ # Allow access to our WWW
+ ${fwcmd} add pass tcp from any to me 80 setup
+
+ # Reject&Log all setup of incoming connections from the outside
+ ${fwcmd} add deny log ip4 from any to any in via ${oif} setup proto tcp
+ if [ -n "$inet6" ]; then
+ ${fwcmd} add deny log ip6 from any to any in via ${oif6} \
+ setup proto tcp
+ fi
+
+ # Allow setup of any other TCP connection
+ ${fwcmd} add pass tcp from any to any setup
+
+ # Allow DNS queries out in the world
+ ${fwcmd} add pass udp from me to any 53 keep-state
+
+ # Allow NTP queries out in the world
+ ${fwcmd} add pass udp from me to any 123 keep-state
+
+ # Everything else is denied by default, unless the
+ # IPFIREWALL_DEFAULT_TO_ACCEPT option is set in your kernel
+ # config file.
+ ;;
+
+[Ww][Oo][Rr][Kk][Ss][Tt][Aa][Tt][Ii][Oo][Nn])
+ # Configuration:
+ # firewall_myservices: List of ports/protocols on which this
+ # host offers services.
+ # firewall_allowservices: List of IPv4 and/or IPv6 addresses
+ # that have access to
+ # $firewall_myservices.
+ # firewall_trusted: List of IPv4 and/or IPv6 addresses
+ # that have full access to this host.
+ # Be very careful when setting this.
+ # This option can seriously degrade
+ # the level of protection provided by
+ # the firewall.
+ # firewall_logdeny: Boolean (YES/NO) specifying if the
+ # default denied packets should be
+ # logged (in /var/log/security).
+ # firewall_nologports: List of TCP/UDP ports for which
+ # denied incoming packets are not
+ # logged.
+
+ # Allow packets for which a state has been built.
+ ${fwcmd} add check-state
+
+ # For services permitted below.
+ ${fwcmd} add pass tcp from me to any established
+
+ # Allow any connection out, adding state for each.
+ ${fwcmd} add pass tcp from me to any setup keep-state
+ ${fwcmd} add pass udp from me to any keep-state
+ ${fwcmd} add pass icmp from me to any keep-state
+ if [ $ipv6_available -eq 0 ]; then
+ ${fwcmd} add pass ipv6-icmp from me to any keep-state
+ fi
+
+ # Allow DHCP.
+ ${fwcmd} add pass udp from 0.0.0.0 68 to 255.255.255.255 67 out
+ ${fwcmd} add pass udp from any 67 to me 68 in
+ ${fwcmd} add pass udp from any 67 to 255.255.255.255 68 in
+ if [ $ipv6_available -eq 0 ]; then
+ ${fwcmd} add pass udp from fe80::/10 to me 546 in
+ fi
+ # Some servers will ping the IP while trying to decide if it's
+ # still in use.
+ ${fwcmd} add pass icmp from any to any icmptype 8
+ if [ $ipv6_available -eq 0 ]; then
+ ${fwcmd} add pass ipv6-icmp from any to any icmp6type 128,129
+ fi
+
+ # Allow "mandatory" ICMP in.
+ ${fwcmd} add pass icmp from any to any icmptype 3,4,11
+ if [ $ipv6_available -eq 0 ]; then
+ ${fwcmd} add pass ipv6-icmp from any to any icmp6type 3
+ fi
+
+ # Add permits for this workstations published services below
+ # Only IPs and nets in firewall_allowservices is allowed in.
+ # If you really wish to let anyone use services on your
+ # workstation, then set "firewall_allowservices='any'" in /etc/rc.conf
+ #
+ # Note: We don't use keep-state as that would allow DoS of
+ # our statetable.
+ # You can add 'keep-state' to the lines for slightly
+ # better performance if you fell that DoS of your
+ # workstation won't be a problem.
+ #
+ for i in ${firewall_allowservices} ; do
+ for j in ${firewall_myservices} ; do
+ case $j in
+ [0-9A-Za-z]*/[Pp][Rr][Oo][Tt][Oo])
+ ${fwcmd} add pass ${j%/[Pp][Rr][Oo][Tt][Oo]} from $i to me
+ ;;
+ [0-9A-Za-z]*/[Tt][Cc][Pp])
+ ${fwcmd} add pass tcp from $i to me ${j%/[Tt][Cc][Pp]}
+ ;;
+ [0-9A-Za-z]*/[Uu][Dd][Pp])
+ ${fwcmd} add pass udp from $i to me ${j%/[Uu][Dd][Pp]}
+ ;;
+ *[0-9A-Za-z])
+ echo "Consider using ${j}/tcp in firewall_myservices." \
+ > /dev/stderr
+ ${fwcmd} add pass tcp from $i to me $j
+ ;;
+ *)
+ echo "Invalid port in firewall_myservices: $j" > /dev/stderr
+ ;;
+ esac
+ done
+ done
+
+ # Allow all connections from trusted IPs.
+ # Playing with the content of firewall_trusted could seriously
+ # degrade the level of protection provided by the firewall.
+ for i in ${firewall_trusted} ; do
+ ${fwcmd} add pass ip from $i to me
+ done
+
+ ${fwcmd} add 65000 count ip from any to any
+
+ # Drop packets to ports where we don't want logging
+ for i in ${firewall_nologports} ; do
+ ${fwcmd} add deny { tcp or udp } from any to any $i in
+ done
+
+ # Broadcasts and multicasts
+ ${fwcmd} add deny ip from any to 255.255.255.255
+ ${fwcmd} add deny ip from any to 224.0.0.0/24 in # XXX
+
+ # Noise from routers
+ ${fwcmd} add deny udp from any to any 520 in
+
+ # Noise from webbrowsing.
+ # The stateful filter is a bit aggressive, and will cause some
+ # connection teardowns to be logged.
+ ${fwcmd} add deny tcp from any 80,443 to any 1024-65535 in
+
+ # Deny and (if wanted) log the rest unconditionally.
+ log=""
+ if [ ${firewall_logdeny:-x} = "YES" -o ${firewall_logdeny:-x} = "yes" ] ; then
+ log="log logamount 500" # The default of 100 is too low.
+ sysctl net.inet.ip.fw.verbose=1 >/dev/null
+ fi
+ ${fwcmd} add deny $log ip from any to any
+ ;;
+
+[Cc][Ll][Oo][Ss][Ee][Dd])
+ ${fwcmd} add 65000 deny ip from any to any
+ ;;
+[Uu][Nn][Kk][Nn][Oo][Ww][Nn])
+ ;;
+*)
+ if [ -r "${firewall_type}" ]; then
+ ${fwcmd} ${firewall_flags} ${firewall_type}
+ fi
+ ;;
+esac
diff --git a/libexec/rc/rc.initdiskless b/libexec/rc/rc.initdiskless
new file mode 100644
index 000000000000..3b66a3c4928a
--- /dev/null
+++ b/libexec/rc/rc.initdiskless
@@ -0,0 +1,408 @@
+#!/bin/sh
+#
+# Copyright (c) 1999 Matt Dillon
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+# On entry to this script the entire system consists of a read-only root
+# mounted via NFS. The kernel has run BOOTP and configured an interface
+# (otherwise it would not have been able to mount the NFS root!)
+#
+# We use the contents of /conf to create and populate memory filesystems
+# that are mounted on top of this root to implement the writable
+# (and host-specific) parts of the root filesystem, and other volatile
+# filesystems.
+#
+# The hierarchy in /conf has the form /conf/T/M/ where M are directories
+# for which memory filesystems will be created and filled,
+# and T is one of the "template" directories below:
+#
+# base universal base, typically a replica of the original root;
+# default secondary universal base, typically overriding some
+# of the files in the original root;
+# ${ipba} where ${ipba} is the assigned broadcast IP address
+# bcast/${ipba} same as above
+# ${class} where ${class} is a list of directories supplied by
+# bootp/dhcp through the T134 option.
+# ${ipba} and ${class} are typically used to configure features
+# for group of diskless clients, or even individual features;
+# ${ip} where ${ip} is the machine's assigned IP address, typically
+# used to set host-specific features;
+# ip/${ip} same as above
+#
+# Template directories are scanned in the order they are listed above,
+# with each successive directory overriding (merged into) the previous one;
+# non-existing directories are ignored. The subdirectory forms exist to
+# help keep the top level /conf manageable in large installations.
+#
+# The existence of a directory /conf/T/M causes this script to create a
+# memory filesystem mounted as /M on the client.
+#
+# Some files in /conf have special meaning, namely:
+#
+# Filename Action
+# ----------------------------------------------------------------
+# /conf/T/M/remount
+# The contents of the file is a mount command. E.g. if
+# /conf/1.2.3.4/foo/remount contains "mount -o ro /dev/ad0s3",
+# then /dev/ad0s3 will be mounted on /conf/1.2.3.4/foo/
+#
+# /conf/T/M/remount_optional
+# If this file exists, then failure to execute the mount
+# command contained in /conf/T/M/remount is non-fatal.
+#
+# /conf/T/M/remount_subdir
+# If this file exists, then the behaviour of /conf/T/M/remount
+# changes as follows:
+# 1. /conf/T/M/remount is invoked to mount the root of the
+# filesystem where the configuration data exists on a
+# temporary mountpoint.
+# 2. /conf/T/M/remount_subdir is then invoked to mount a
+# *subdirectory* of the filesystem mounted by
+# /conf/T/M/remount on /conf/T/M/.
+#
+# /conf/T/M/diskless_remount
+# The contents of the file points to an NFS filesystem,
+# possibly followed by mount_nfs options. If the server name
+# is omitted, the script will prepend the root path used when
+# booting. E.g. if you booted from foo.com:/path/to/root,
+# an entry for /conf/base/etc/diskless_remount could be any of
+# foo.com:/path/to/root/etc
+# /etc -o ro
+# Because mount_nfs understands ".." in paths, it is
+# possible to mount from locations above the NFS root with
+# paths such as "/../../etc".
+#
+# /conf/T/M/md_size
+# The contents of the file specifies the size of the memory
+# filesystem to be created, in 512 byte blocks.
+# The default size is 10240 blocks (5MB). E.g. if
+# /conf/base/etc/md_size contains "30000" then a 15MB MFS
+# will be created. In case of multiple entries for the same
+# directory M, the last one in the scanning order is used.
+# NOTE: If you only need to create a memory filesystem but not
+# initialize it from a template, it is preferable to specify
+# it in fstab e.g. as "md /tmp mfs -s=30m,rw 0 0"
+#
+# /conf/T/SUBDIR.cpio.gz
+# The file is cpio'd into /SUBDIR (and a memory filesystem is
+# created for /SUBDIR if necessary). The presence of this file
+# prevents the copy from /conf/T/SUBDIR/
+#
+# /conf/T/M/extract
+# This is alternative to SUBDIR.cpio.gz and remount.
+# Similar to remount case, a memory filesystem is created
+# for /M and initialized from a template but no mounting
+# performed. Instead, this file is run passing /M as single
+# argument. It is expected to extract template override to /M
+# using auxiliary storage found in some embedded systems
+# having NVRAM too small to hold mountable file system.
+#
+# /conf/T/SUBDIR.remove
+# The list of paths contained in the file are rm -rf'd
+# relative to /SUBDIR.
+#
+# /conf/diskless_remount
+# Similar to /conf/T/M/diskless_remount above, but allows
+# all of /conf to be remounted. This can be used to allow
+# multiple roots to share the same /conf.
+#
+#
+# You will almost universally want to create the following files under /conf
+#
+# File Content
+# ---------------------------- ----------------------------------
+# /conf/base/etc/md_size size of /etc filesystem
+# /conf/base/etc/diskless_remount "/etc"
+# /conf/default/etc/rc.conf generic diskless config parameters
+# /conf/default/etc/fstab generic diskless fstab e.g. like this
+#
+# foo:/root_part / nfs ro 0 0
+# foo:/usr_part /usr nfs ro 0 0
+# foo:/home_part /home nfs rw 0 0
+# md /tmp mfs -s=30m,rw 0 0
+# md /var mfs -s=30m,rw 0 0
+# proc /proc procfs rw 0 0
+#
+# plus, possibly, overrides for password files etc.
+#
+# NOTE! /var, /tmp, and /dev will be typically created elsewhere, e.g.
+# as entries in the fstab as above.
+# Those filesystems should not be specified in /conf.
+#
+# (end of documentation, now get to the real code)
+
+dlv=`/sbin/sysctl -n vfs.nfs.diskless_valid 2> /dev/null`
+
+# DEBUGGING
+# log something on stdout if verbose.
+o_verbose=0 # set to 1 or 2 if you want more debugging
+log() {
+ [ ${o_verbose} -gt 0 ] && echo "*** $* ***"
+ [ ${o_verbose} -gt 1 ] && read -p "=== Press enter to continue" foo
+}
+
+# chkerr:
+#
+# Routine to check for error
+#
+# checks error code and drops into shell on failure.
+# if shell exits, terminates script as well as /etc/rc.
+# if remount_optional exists under the mountpoint, skip this check.
+#
+chkerr() {
+ lastitem () ( n=$(($# - 1)) ; shift $n ; echo $1 )
+ mountpoint="$(lastitem $2)"
+ if [ -r $mountpoint/remount_optional ]; then
+ echo "$2 failed: ignoring due to remount_optional"
+ return
+ fi
+ case $1 in
+ 0)
+ ;;
+ *)
+ echo "$2 failed: dropping into /bin/sh"
+ /bin/sh
+ # RESUME
+ ;;
+ esac
+}
+
+# The list of filesystems to umount after the copy
+to_umount=""
+
+handle_remount() { # $1 = mount point
+ local nfspt mountopts b
+ b=$1
+ log handle_remount $1
+ [ -d $b -a -f $b/diskless_remount ] || return
+ read nfspt mountopts < $b/diskless_remount
+ log "nfspt ${nfspt} mountopts ${mountopts}"
+ # prepend the nfs root if not present
+ [ `expr "$nfspt" : '\(.\)'` = "/" ] && nfspt="${nfsroot}${nfspt}"
+ mount_nfs $mountopts $nfspt $b
+ chkerr $? "mount_nfs $nfspt $b"
+ to_umount="$b ${to_umount}"
+}
+
+# Create a generic memory disk.
+# The 'auto' parameter will attempt to use tmpfs(4), falls back to md(4).
+# $1 is size in 512-byte sectors, $2 is the mount point.
+mount_md() {
+ if [ ${o_verbose} -gt 0 ] ; then
+ /sbin/mdmfs -XL -S -s $1 auto $2
+ else
+ /sbin/mdmfs -S -s $1 auto $2
+ fi
+}
+
+# Create the memory filesystem if it has not already been created
+#
+create_md() {
+ [ "x`eval echo \\$md_created_$1`" = "x" ] || return # only once
+ if [ "x`eval echo \\$md_size_$1`" = "x" ]; then
+ md_size=10240
+ else
+ md_size=`eval echo \\$md_size_$1`
+ fi
+ log create_md $1 with size $md_size
+ mount_md $md_size /$1
+ /bin/chmod 755 /$1
+ eval md_created_$1=created
+}
+
+# DEBUGGING
+#
+# set -v
+
+# Figure out our interface and IP.
+#
+bootp_ifc=""
+bootp_ipa=""
+bootp_ipbca=""
+class=""
+if [ ${dlv:=0} -ne 0 ] ; then
+ iflist=`ifconfig -l`
+ for i in ${iflist} ; do
+ set -- `ifconfig ${i}`
+ while [ $# -ge 1 ] ; do
+ if [ "${bootp_ifc}" = "" -a "$1" = "inet" ] ; then
+ bootp_ifc=${i} ; bootp_ipa=${2} ; shift
+ fi
+ if [ "${bootp_ipbca}" = "" -a "$1" = "broadcast" ] ; then
+ bootp_ipbca=$2; shift
+ fi
+ shift
+ done
+ if [ "${bootp_ifc}" != "" ] ; then
+ break
+ fi
+ done
+ # Get the values passed with the T134 bootp cookie.
+ class="`/sbin/sysctl -qn kern.bootp_cookie`"
+
+ echo "Interface ${bootp_ifc} IP-Address ${bootp_ipa} Broadcast ${bootp_ipbca} ${class}"
+fi
+
+log Figure out our NFS root path
+#
+set -- `mount -t nfs`
+while [ $# -ge 1 ] ; do
+ if [ "$2" = "on" -a "$3" = "/" ]; then
+ nfsroot="$1"
+ break
+ fi
+ shift
+done
+
+# The list of directories with template files
+templates="base default"
+if [ -n "${bootp_ipbca}" ]; then
+ templates="${templates} ${bootp_ipbca} bcast/${bootp_ipbca}"
+fi
+if [ -n "${class}" ]; then
+ templates="${templates} ${class}"
+fi
+if [ -n "${bootp_ipa}" ]; then
+ templates="${templates} ${bootp_ipa} ip/${bootp_ipa}"
+fi
+
+# If /conf/diskless_remount exists, remount all of /conf.
+handle_remount /conf
+
+# Resolve templates in /conf/base, /conf/default, /conf/${bootp_ipbca},
+# and /conf/${bootp_ipa}. For each subdirectory found within these
+# directories:
+#
+# - calculate memory filesystem sizes. If the subdirectory (prior to
+# NFS remounting) contains the file 'md_size', the contents specified
+# in 512 byte sectors will be used to size the memory filesystem. Otherwise
+# 8192 sectors (4MB) is used.
+#
+# - handle NFS remounts. If the subdirectory contains the file
+# diskless_remount, the contents of the file is NFS mounted over
+# the directory. For example /conf/base/etc/diskless_remount
+# might contain 'myserver:/etc'. NFS remounts allow you to avoid
+# having to dup your system directories in /conf. Your server must
+# be sure to export those filesystems -alldirs, however.
+# If the diskless_remount file contains a string beginning with a
+# '/' it is assumed that the local nfsroot should be prepended to
+# it before attempting to the remount. This allows the root to be
+# relocated without needing to change the remount files.
+#
+log "templates are ${templates}"
+for i in ${templates} ; do
+ for j in /conf/$i/* ; do
+ [ -d $j ] || continue
+
+ # memory filesystem size specification
+ subdir=${j##*/}
+ [ -f $j/md_size ] && eval md_size_$subdir=`cat $j/md_size`
+
+ # remount. Beware, the command is in the file itself!
+ if [ -f $j/remount ]; then
+ if [ -f $j/remount_subdir ]; then
+ k="/conf.tmp/$i/$subdir"
+ [ -d $k ] || continue
+
+ # Mount the filesystem root where the config data is
+ # on the temporary mount point.
+ nfspt=`/bin/cat $j/remount`
+ $nfspt $k
+ chkerr $? "$nfspt $k"
+
+ # Now use a nullfs mount to get the data where we
+ # really want to see it.
+ remount_subdir=`/bin/cat $j/remount_subdir`
+ remount_subdir_cmd="mount -t nullfs $k/$remount_subdir"
+
+ $remount_subdir_cmd $j
+ chkerr $? "$remount_subdir_cmd $j"
+
+ # XXX check order -- we must force $k to be unmounted
+ # after j, as j depends on k.
+ to_umount="$j $k ${to_umount}"
+ else
+ nfspt=`/bin/cat $j/remount`
+ $nfspt $j
+ chkerr $? "$nfspt $j"
+ to_umount="$j ${to_umount}" # XXX hope it is really a mount!
+ fi
+ fi
+
+ # NFS remount
+ handle_remount $j
+ done
+done
+
+# - Create all required MFS filesystems and populate them from
+# our templates. Support both a direct template and a dir.cpio.gz
+# archive. Support for auxiliary NVRAM. Support dir.remove files containing
+# a list of relative paths to remove.
+#
+# The dir.cpio.gz form is there to make the copy process more efficient,
+# so if the cpio archive is present, it prevents the files from dir/
+# from being copied.
+
+PATH=${PATH}:/rescue
+
+for i in ${templates} ; do
+ for j in /conf/$i/* ; do
+ subdir=${j##*/}
+ if [ -d $j -a ! -f $j.cpio.gz ]; then
+ create_md $subdir
+ cp -Rp $j/ /$subdir
+ fi
+ done
+ for j in /conf/$i/*.cpio.gz ; do
+ subdir=${j%*.cpio.gz}
+ subdir=${subdir##*/}
+ if [ -f $j ]; then
+ create_md $subdir
+ echo "Loading /$subdir from cpio archive $j"
+ (cd / ; tar -xpf $j)
+ fi
+ done
+ for j in /conf/$i/*/extract ; do
+ if [ -x $j ]; then
+ subdir=${j%*/extract}
+ subdir=${subdir##*/}
+ create_md $subdir
+ echo "Loading /$subdir using auxiliary command $j"
+ $j /$subdir
+ fi
+ done
+ for j in /conf/$i/*.remove ; do
+ subdir=${j%*.remove}
+ subdir=${subdir##*/}
+ if [ -f $j ]; then
+ # doubly sure it is a memory disk before rm -rf'ing
+ create_md $subdir
+ (cd /$subdir; rm -rf `/bin/cat $j`)
+ fi
+ done
+done
+
+# umount partitions used to fill the memory filesystems
+[ -n "${to_umount}" ] && umount $to_umount
diff --git a/libexec/rc/rc.resume b/libexec/rc/rc.resume
new file mode 100755
index 000000000000..147bc2ba4f8d
--- /dev/null
+++ b/libexec/rc/rc.resume
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# Copyright (c) 1999 Mitsuru IWASAKI
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# sample run command file for APM Resume Event
+
+if [ $# -ne 2 ]; then
+ echo "Usage: $0 [apm|acpi] [standby,suspend|1-4]"
+ exit 1
+fi
+
+subsystem=$1
+state=$2
+
+if [ -r /var/run/rc.suspend.pid ]; then
+ kill -9 `cat /var/run/rc.suspend.pid`
+ /bin/rm -f /var/run/rc.suspend.pid
+ echo 'rc.resume: killed rc.suspend that was still around'
+fi
+
+# If a device driver has problems resuming, try unloading it before
+# suspend and reloading it on resume. Example:
+# kldload usb
+
+/usr/bin/logger -t $subsystem resumed at `/bin/date +'%Y%m%d %H:%M:%S'`
+/bin/sync && /bin/sync && /bin/sync
+
+. /etc/rc.subr
+
+load_rc_config
+
+rcorder_opts="-k resume"
+
+case ${local_startup} in
+[Nn][Oo] | '') ;;
+*) find_local_scripts_new ;;
+esac
+
+files=`rcorder ${rcorder_opts} /etc/rc.d/* ${local_rc} 2>/dev/null`
+
+for _rc_elem in $files; do
+ debug "run_rc_script $_rc_elem resume"
+ run_rc_script $_rc_elem resume
+done
+
+exit 0
diff --git a/libexec/rc/rc.shutdown b/libexec/rc/rc.shutdown
new file mode 100644
index 000000000000..3dfd7a7e0936
--- /dev/null
+++ b/libexec/rc/rc.shutdown
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# Copyright (c) 1997 Ollivier Robert
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# Site-specific closing actions for daemons run by init on shutdown,
+# or before going single-user from multi-user.
+# Output and errors are directed to console by init, and the
+# console is the controlling terminal.
+
+stty status '^T' 2> /dev/null
+
+# Set shell to ignore SIGINT (2), but not children;
+# shell catches SIGQUIT (3) and returns to single user after fsck.
+trap : 2
+trap : 3 # shouldn't be needed
+
+HOME=/
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin
+export HOME PATH
+
+rc_shutdown=${1:-"unspecified"}
+
+. /etc/rc.subr
+
+load_rc_config
+
+# reverse_list list
+# print the list in reverse order
+#
+reverse_list()
+{
+ _revlist=
+ for _revfile in $*; do
+ _revlist="$_revfile${script_name_sep}$_revlist"
+ done
+ echo $_revlist
+}
+
+# If requested, start a watchdog timer in the background which
+# will terminate rc.shutdown if rc.shutdown doesn't complete
+# within the specified time.
+#
+_rcshutdown_watchdog=
+if [ -n "$rcshutdown_timeout" ]; then
+ debug "Initiating watchdog timer."
+ sleep $rcshutdown_timeout && (
+ _msg="$rcshutdown_timeout second watchdog"
+ _msg="$_msg timeout expired. Shutdown terminated."
+ logger -t rc.shutdown "$_msg"
+ echo
+ echo "$_msg"
+ date
+ kill -KILL $$ >/dev/null 2>&1
+ ) &
+ _rcshutdown_watchdog=$!
+fi
+
+# Determine the shutdown order of the /etc/rc.d scripts,
+# and perform the operation
+#
+rcorder_opts="-k shutdown"
+if check_jail jailed; then
+ rcorder_opts="$rcorder_opts -s nojail"
+ if ! check_jail vnet; then
+ rcorder_opts="$rcorder_opts -s nojailvnet"
+ fi
+fi
+
+case ${local_startup} in
+[Nn][Oo] | '') ;;
+*) find_local_scripts_new ;;
+esac
+
+files=`rcorder ${rcorder_opts} /etc/rc.d/* ${local_rc} 2>/dev/null`
+
+for _rc_elem in `reverse_list $files`; do
+ debug "run_rc_script $_rc_elem faststop"
+ run_rc_script $_rc_elem faststop
+done
+
+# Terminate the background watchdog timer (if it is running)
+#
+if [ -n "$_rcshutdown_watchdog" ]; then
+ pkill -TERM -P $_rcshutdown_watchdog >/dev/null 2>&1
+fi
+
+# Insert other shutdown procedures here
+
+
+echo '.'
+exit 0
diff --git a/libexec/rc/rc.subr b/libexec/rc/rc.subr
new file mode 100644
index 000000000000..e4ad14f582d6
--- /dev/null
+++ b/libexec/rc/rc.subr
@@ -0,0 +1,2880 @@
+# $NetBSD: rc.subr,v 1.67 2006/10/07 11:25:15 elad Exp $
+#
+# Copyright (c) 1997-2004 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Luke Mewburn.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# rc.subr
+# functions used by various rc scripts
+#
+
+: ${RC_PID:=$$}; export RC_PID
+
+#
+# Operating System dependent/independent variables
+#
+
+if [ -n "${_rc_subr_loaded}" ]; then
+ return
+fi
+
+_rc_subr_loaded="YES"
+
+SYSCTL="/sbin/sysctl"
+SYSCTL_N="${SYSCTL} -n"
+SYSCTL_W="${SYSCTL}"
+PROTECT="/usr/bin/protect"
+ID="/usr/bin/id"
+IDCMD="if [ -x $ID ]; then $ID -un; fi"
+PS="/bin/ps -ww"
+SERVICE=/usr/sbin/service
+JAIL_CMD=/usr/sbin/jail
+_svcj_generic_params="path=/ mount.nodevfs host=inherit"
+JID=0
+CPUSET="/bin/cpuset"
+
+# Cache the services that we loaded with load_rc_config.
+_loaded_services=""
+
+# rc_service provides the path to the service script that we are executing.
+# This is not being set here in an execution context, necessarily, so it's
+# really just a reasonable guess, and it will get overwritten later if
+# we are executing from some other means than direct execution by service(8)
+# or manual invocation of the service script. The prime example of this is
+# during system startup, all rc scripts will be invoked via /etc/rc, so
+# run_rc_script will overwrite rc_service with the file being sourced.
+rc_service="$0"
+
+#
+# functions
+# ---------
+
+# is_verified file
+# if VERIEXEC is active check that $file is verified
+#
+VERIEXEC="/sbin/veriexec"
+if test -x $VERIEXEC && $VERIEXEC -i active > /dev/null 2>&1; then
+ is_verified() { $VERIEXEC -x $1; }
+else
+ is_verified() { return 0; }
+fi
+
+# indicate that we have vdot
+_VDOT_SH=:
+
+# current state of O_VERIFY
+o_verify()
+{
+ case $(echo $(set -o)) in
+ *verify" "off*) echo off;;
+ *verify" "on*) echo on;;
+ esac
+}
+
+##
+# o_verify_set want [save]
+#
+# record current state of verify in $save
+# and set it to $want if different
+#
+o_verify_set() {
+ local x=$(o_verify)
+
+ [ -z "$x" ] && return 0
+ [ -z "$2" ] || eval $2=$x
+ [ "$x" = "$1" ] && return 0
+ case "$1" in
+ on)
+ set -o verify
+ ;;
+ off)
+ set +o verify
+ ;;
+ esac
+}
+
+# for unverified files
+dotted=
+dot()
+{
+ local f verify
+ local dot_dir dot_file
+
+ o_verify_set off verify
+ for f in "$@"; do
+ if [ -f $f -a -s $f ]; then
+ dotted="$dotted $f"
+ case $f in
+ */*)
+ dot_dir=${f%/*}
+ dot_file=${f##*/}
+ ;;
+ *)
+ dot_dir=.
+ dot_file=$f
+ ;;
+ esac
+ . $f
+ fi
+ done
+ o_verify_set $verify
+}
+
+# try for verified, fallback to safe
+sdot()
+{
+ local f
+
+ for f in "$@"; do
+ [ -f $f -a -s $f ] || continue
+ vdot $f || safe_dot $f
+ done
+}
+
+# convenience function - skip if not verified
+vdot()
+{
+ local f rc=0 verify
+
+ o_verify_set on verify
+ for f in "$@"; do
+ [ -f $f -a -s $f ] || continue
+ if is_verified $f 2> /dev/null; then
+ dot $f
+ else
+ rc=80 # EAUTH
+ fi
+ done
+ o_verify_set $verify
+ return $rc
+}
+
+# Exists [test] file ...
+# report the first "file" that passes "test" (default -s).
+Exists()
+{
+ local f _t=-s
+
+ while :; do
+ : 1=$1
+ case "$1" in
+ -?)
+ _t=$1
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ for f in "$@"; do
+ [ $_t $f ] || continue
+ echo $f
+ return 0
+ done
+ return 1
+}
+
+# do we have $1 (could be a function)
+have()
+{
+ type "$1" > /dev/null 2>&1
+}
+
+# provide consistent means of logging progress
+rc_log()
+{
+ date "+@ %s [%Y-%m-%d %H:%M:%S %Z] $*"
+}
+
+# only rc_log if tracing enabled
+# and $level >= $RC_LEVEL
+rc_trace()
+{
+ local level=$1; shift
+ local cf=/etc/rc.conf.d/rc_trace
+
+ if [ -z "$RC_LEVEL" ]; then
+ [ -f $cf ] || return
+ RC_LEVEL=0 # existence is 0 at least
+ sdot $cf # allow override
+ fi
+ [ ${RC_LEVEL:-0} -ge ${level:-0} ] || return
+ rc_log "$@"
+}
+
+# list_vars pattern
+# List variables matching glob pattern.
+#
+list_vars()
+{
+ # Localize 'set' option below.
+ local -
+ local IFS=$'\n' line varname
+
+ # Disable path expansion in unquoted 'for' parameters below.
+ set -o noglob
+
+ for line in $(set); do
+ varname="${line%%=*}"
+
+ case "$varname" in
+ "$line"|*[!a-zA-Z0-9_]*)
+ continue
+ ;;
+ $1)
+ echo $varname
+ ;;
+ esac
+ done
+}
+
+# set_rcvar [var] [defval] [desc]
+#
+# Echo or define a rc.conf(5) variable name. Global variable
+# $rcvars is used.
+#
+# If no argument is specified, echo "${name}_enable".
+#
+# If only a var is specified, echo "${var}_enable".
+#
+# If var and defval are specified, the ${var} is defined as
+# rc.conf(5) variable and the default value is ${defvar}. An
+# optional argument $desc can also be specified to add a
+# description for that.
+#
+set_rcvar()
+{
+ local _var
+
+ case $# in
+ 0) echo ${name}_enable ;;
+ 1) echo ${1}_enable ;;
+ *)
+ debug "set_rcvar: \$$1=$2 is added" \
+ " as a rc.conf(5) variable."
+ _var=$1
+ rcvars="${rcvars# } $_var"
+ eval ${_var}_defval=\"$2\"
+ shift 2
+ eval ${_var}_desc=\"$*\"
+ ;;
+ esac
+}
+
+# set_rcvar_obsolete oldvar [newvar] [msg]
+# Define obsolete variable.
+# Global variable $rcvars_obsolete is used.
+#
+set_rcvar_obsolete()
+{
+ local _var
+ _var=$1
+ debug "set_rcvar_obsolete: \$$1(old) -> \$$2(new) is defined"
+
+ rcvars_obsolete="${rcvars_obsolete# } $1"
+ eval ${1}_newvar=\"$2\"
+ shift 2
+ eval ${_var}_obsolete_msg=\"$*\"
+}
+
+#
+# force_depend script [rcvar]
+# Force a service to start. Intended for use by services
+# to resolve dependency issues.
+# $1 - filename of script, in /etc/rc.d, to run
+# $2 - name of the script's rcvar (minus the _enable)
+#
+force_depend()
+{
+ local _depend _dep_rcvar
+
+ _depend="$1"
+ _dep_rcvar="${2:-$1}_enable"
+
+ [ -n "$rc_fast" ] && ! checkyesno always_force_depends &&
+ checkyesno $_dep_rcvar && return 0
+
+ /etc/rc.d/${_depend} forcestatus >/dev/null 2>&1 && return 0
+
+ info "${name} depends on ${_depend}, which will be forced to start."
+ if ! /etc/rc.d/${_depend} forcestart; then
+ warn "Unable to force ${_depend}. It may already be running."
+ return 1
+ fi
+}
+
+#
+# checkyesno var
+# Test $1 variable, and warn if not set to YES or NO.
+# Return 0 if it's "yes" (et al), nonzero otherwise.
+#
+checkyesno()
+{
+ eval _value=\$${1}
+ debug "checkyesno: $1 is set to $_value."
+ case $_value in
+
+ # "yes", "true", "on", or "1"
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+ return 0
+ ;;
+
+ # "no", "false", "off", or "0"
+ [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
+ return 1
+ ;;
+ *)
+ warn "\$${1} is not set properly - see rc.conf(5)."
+ return 1
+ ;;
+ esac
+}
+
+#
+# reverse_list list
+# print the list in reverse order
+#
+reverse_list()
+{
+ _revlist=
+ for _revfile; do
+ _revlist="$_revfile $_revlist"
+ done
+ echo $_revlist
+}
+
+# stop_boot always
+# If booting directly to multiuser or $always is enabled,
+# send SIGTERM to the parent (/etc/rc) to abort the boot.
+# Otherwise just exit.
+#
+stop_boot()
+{
+ local always
+
+ case $1 in
+ # "yes", "true", "on", or "1"
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+ always=true
+ ;;
+ *)
+ always=false
+ ;;
+ esac
+ if [ "$autoboot" = yes -o "$always" = true ]; then
+ echo "ERROR: ABORTING BOOT (sending SIGTERM to parent)!"
+ kill -TERM ${RC_PID}
+ fi
+ exit 1
+}
+
+#
+# mount_critical_filesystems type
+# Go through the list of critical filesystems as provided in
+# the rc.conf(5) variable $critical_filesystems_${type}, checking
+# each one to see if it is mounted, and if it is not, mounting it.
+#
+mount_critical_filesystems()
+{
+ eval _fslist=\$critical_filesystems_${1}
+ for _fs in $_fslist; do
+ mount | (
+ _ismounted=false
+ while read what _on on _type type; do
+ if [ $on = $_fs ]; then
+ _ismounted=true
+ fi
+ done
+ if $_ismounted; then
+ :
+ else
+ mount $_fs >/dev/null 2>&1
+ fi
+ )
+ done
+}
+
+#
+# check_pidfile pidfile procname [interpreter]
+# Parses the first line of pidfile for a PID, and ensures
+# that the process is running and matches procname.
+# Prints the matching PID upon success, nothing otherwise.
+# interpreter is optional; see _find_processes() for details.
+#
+check_pidfile()
+{
+ _pidfile=$1
+ _procname=$2
+ _interpreter=$3
+ if [ -z "$_pidfile" -o -z "$_procname" ]; then
+ err 3 'USAGE: check_pidfile pidfile procname [interpreter]'
+ fi
+ if [ ! -f $_pidfile ]; then
+ debug "pid file ($_pidfile): not readable."
+ return
+ fi
+ read _pid _junk < $_pidfile
+ if [ -z "$_pid" ]; then
+ debug "pid file ($_pidfile): no pid in file."
+ return
+ fi
+ _find_processes $_procname ${_interpreter:-.} '-p '"$_pid"
+}
+
+#
+# check_process procname [interpreter]
+# Ensures that a process (or processes) named procname is running.
+# Prints a list of matching PIDs.
+# interpreter is optional; see _find_processes() for details.
+#
+check_process()
+{
+ _procname=$1
+ _interpreter=$2
+ if [ -z "$_procname" ]; then
+ err 3 'USAGE: check_process procname [interpreter]'
+ fi
+ _find_processes $_procname ${_interpreter:-.} '-ax'
+}
+
+#
+# _find_processes procname interpreter psargs
+# Search for procname in the output of ps generated by psargs.
+# Prints the PIDs of any matching processes, space separated.
+#
+# If interpreter == ".", check the following variations of procname
+# against the first word of each command:
+# procname
+# `basename procname`
+# `basename procname` + ":"
+# "(" + `basename procname` + ")"
+# "[" + `basename procname` + "]"
+#
+# If interpreter != ".", read the first line of procname, remove the
+# leading #!, normalise whitespace, append procname, and attempt to
+# match that against each command, either as is, or with extra words
+# at the end. As an alternative, to deal with interpreted daemons
+# using perl, the basename of the interpreter plus a colon is also
+# tried as the prefix to procname.
+#
+_find_processes()
+{
+ if [ $# -ne 3 ]; then
+ err 3 'USAGE: _find_processes procname interpreter psargs'
+ fi
+ _procname=$1
+ _interpreter=$2
+ _psargs=$3
+
+ _pref=
+ if [ $_interpreter != "." ]; then # an interpreted script
+ _script="${_chroot}${_chroot:+/}$_procname"
+ if [ -r "$_script" ]; then
+ read _interp < $_script # read interpreter name
+ case "$_interp" in
+ \#!*)
+ _interp=${_interp#\#!} # strip #!
+ set -- $_interp
+ case $1 in
+ */bin/env)
+ shift # drop env to get real name
+ ;;
+ esac
+ if [ $_interpreter != $1 ]; then
+ warn "\$command_interpreter $_interpreter != $1"
+ fi
+ ;;
+ *)
+ warn "no shebang line in $_script"
+ set -- $_interpreter
+ ;;
+ esac
+ else
+ warn "cannot read shebang line from $_script"
+ set -- $_interpreter
+ fi
+ _interp="$* $_procname" # cleanup spaces, add _procname
+ _interpbn=${1##*/}
+ _fp_args='_argv'
+ _fp_match='case "$_argv" in
+ ${_interp}|"${_interp} "*|"[${_interpbn}]"|"${_interpbn}: ${_procname}"*)'
+ else # a normal daemon
+ _procnamebn=${_procname##*/}
+ _fp_args='_arg0 _argv'
+ _fp_match='case "$_arg0" in
+ $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})"|"[${_procnamebn}]")'
+ fi
+
+ if checkyesno ${name}_svcj && [ "${_rc_svcj}" != jailing ]; then
+ JID=$(/usr/sbin/jls -j svcj-${name} jid 2>/dev/null)
+
+ case ${JID} in
+ ''|*[!0-9]*)
+ # svcj-jail doesn't exist, fallback to host-check
+ JID=0
+ ;;
+ esac
+ fi
+ _proccheck="\
+ $PS 2>/dev/null -o pid= -o jid= -o command= $_psargs"' |
+ while read _npid _jid '"$_fp_args"'; do
+ '"$_fp_match"'
+ if [ "$JID" -eq "$_jid" ];
+ then echo -n "$_pref$_npid";
+ _pref=" ";
+ fi
+ ;;
+ esac
+ done'
+
+# debug "in _find_processes: proccheck is ($_proccheck)."
+ eval $_proccheck
+}
+
+# sort_lite [-b] [-n] [-k POS] [-t SEP]
+# A lite version of sort(1) (supporting a few options) that can be used
+# before the real sort(1) is available (e.g., in scripts that run prior
+# to mountcritremote). Requires only shell built-in functionality.
+#
+sort_lite()
+{
+ local funcname=sort_lite
+ local sort_sep="$IFS" sort_ignore_leading_space=
+ local sort_field=0 sort_strict_fields= sort_numeric=
+ local nitems=0 skip_leading=0 trim=
+
+ local OPTIND flag
+ while getopts bnk:t: flag; do
+ case "$flag" in
+ b) sort_ignore_leading_space=1 ;;
+ n) sort_numeric=1 sort_ignore_leading_space=1 ;;
+ k) sort_field="${OPTARG%%,*}" ;; # only up to first comma
+ # NB: Unlike sort(1) only one POS allowed
+ t) sort_sep="$OPTARG"
+ if [ ${#sort_sep} -gt 1 ]; then
+ echo "$funcname: multi-character tab \`$sort_sep'" >&2
+ return 1
+ fi
+ sort_strict_fields=1
+ ;;
+ \?) return 1 ;;
+ esac
+ done
+ shift $(( $OPTIND - 1 ))
+
+ # Create transformation pattern to trim leading text if desired
+ case "$sort_field" in
+ ""|[!0-9]*|*[!0-9.]*)
+ echo "$funcname: invalid sort field \`$sort_field'" >&2
+ return 1
+ ;;
+ *.*)
+ skip_leading=${sort_field#*.} sort_field=${sort_field%%.*}
+ while [ ${skip_leading:-0} -gt 1 ] 2> /dev/null; do
+ trim="$trim?" skip_leading=$(( $skip_leading - 1 ))
+ done
+ esac
+
+ # Copy input to series of local numbered variables
+ # NB: IFS of NULL preserves leading whitespace
+ local LINE
+ while IFS= read -r LINE || [ "$LINE" ]; do
+ nitems=$(( $nitems + 1 ))
+ local src_$nitems="$LINE"
+ done
+
+ #
+ # Sort numbered locals using insertion sort
+ #
+ local curitem curitem_orig curitem_mod curitem_haskey
+ local dest dest_orig dest_mod dest_haskey
+ local d gt n
+ local i=1
+ while [ $i -le $nitems ]; do
+ curitem_haskey=1 # Assume sort field (-k POS) exists
+ eval curitem=\"\$src_$i\"
+ curitem_mod="$curitem" # for modified comparison
+ curitem_orig="$curitem" # for original comparison
+
+ # Trim leading whitespace if desired
+ if [ "$sort_ignore_leading_space" ]; then
+ while case "$curitem_orig" in
+ [$IFS]*) : ;; *) false; esac
+ do
+ curitem_orig="${curitem_orig#?}"
+ done
+ curitem_mod="$curitem_orig"
+ fi
+
+ # Shift modified comparison value if sort field (-k POS) is > 1
+ n=$sort_field
+ while [ $n -gt 1 ]; do
+ case "$curitem_mod" in
+ *[$sort_sep]*)
+ # Cut text up-to (and incl.) first separator
+ curitem_mod="${curitem_mod#*[$sort_sep]}"
+
+ # Skip NULLs unless strict field splitting
+ [ "$sort_strict_fields" ] ||
+ [ "${curitem_mod%%[$sort_sep]*}" ] ||
+ [ $n -eq 2 ] ||
+ continue
+ ;;
+ *)
+ # Asked for a field that doesn't exist
+ curitem_haskey= break
+ esac
+ n=$(( $n - 1 ))
+ done
+
+ # Trim trailing words if sort field >= 1
+ [ $sort_field -ge 1 -a "$sort_numeric" ] &&
+ curitem_mod="${curitem_mod%%[$sort_sep]*}"
+
+ # Apply optional trim (-k POS.TRIM) to cut leading characters
+ curitem_mod="${curitem_mod#$trim}"
+
+ # Determine the type of modified comparison to use initially
+ # NB: Prefer numerical if requested but fallback to standard
+ case "$curitem_mod" in
+ ""|[!0-9]*) # NULL or begins with non-number
+ gt=">"
+ [ "$sort_numeric" ] && curitem_mod=0
+ ;;
+ *)
+ if [ "$sort_numeric" ]; then
+ gt="-gt"
+ curitem_mod="${curitem_mod%%[!0-9]*}"
+ # NB: trailing non-digits removed
+ # otherwise numeric comparison fails
+ else
+ gt=">"
+ fi
+ esac
+
+ # If first time through, short-circuit below position-search
+ if [ $i -le 1 ]; then
+ d=0
+ else
+ d=1
+ fi
+
+ #
+ # Find appropriate element position
+ #
+ while [ $d -gt 0 ]
+ do
+ dest_haskey=$curitem_haskey
+ eval dest=\"\$dest_$d\"
+ dest_mod="$dest" # for modified comparison
+ dest_orig="$dest" # for original comparison
+
+ # Trim leading whitespace if desired
+ if [ "$sort_ignore_leading_space" ]; then
+ while case "$dest_orig" in
+ [$IFS]*) : ;; *) false; esac
+ do
+ dest_orig="${dest_orig#?}"
+ done
+ dest_mod="$dest_orig"
+ fi
+
+ # Shift modified value if sort field (-k POS) is > 1
+ n=$sort_field
+ while [ $n -gt 1 ]; do
+ case "$dest_mod" in
+ *[$sort_sep]*)
+ # Cut text up-to (and incl.) 1st sep
+ dest_mod="${dest_mod#*[$sort_sep]}"
+
+ # Skip NULLs unless strict fields
+ [ "$sort_strict_fields" ] ||
+ [ "${dest_mod%%[$sort_sep]*}" ] ||
+ [ $n -eq 2 ] ||
+ continue
+ ;;
+ *)
+ # Asked for a field that doesn't exist
+ dest_haskey= break
+ esac
+ n=$(( $n - 1 ))
+ done
+
+ # Trim trailing words if sort field >= 1
+ [ $sort_field -ge 1 -a "$sort_numeric" ] &&
+ dest_mod="${dest_mod%%[$sort_sep]*}"
+
+ # Apply optional trim (-k POS.TRIM), cut leading chars
+ dest_mod="${dest_mod#$trim}"
+
+ # Determine type of modified comparison to use
+ # NB: Prefer numerical if requested, fallback to std
+ case "$dest_mod" in
+ ""|[!0-9]*) # NULL or begins with non-number
+ gt=">"
+ [ "$sort_numeric" ] && dest_mod=0
+ ;;
+ *)
+ if [ "$sort_numeric" ]; then
+ gt="-gt"
+ dest_mod="${dest_mod%%[!0-9]*}"
+ # NB: kill trailing non-digits
+ # for numeric comparison safety
+ else
+ gt=">"
+ fi
+ esac
+
+ # Break if we've found the proper element position
+ if [ "$curitem_haskey" -a "$dest_haskey" ]; then
+ if [ "$dest_mod" = "$curitem_mod" ]; then
+ [ "$dest_orig" ">" "$curitem_orig" ] &&
+ break
+ elif [ "$dest_mod" $gt "$curitem_mod" ] \
+ 2> /dev/null
+ then
+ break
+ fi
+ else
+ [ "$dest_orig" ">" "$curitem_orig" ] && break
+ fi
+
+ # Break if we've hit the end
+ [ $d -ge $i ] && break
+
+ d=$(( $d + 1 ))
+ done
+
+ # Shift remaining positions forward, making room for new item
+ n=$i
+ while [ $n -ge $d ]; do
+ # Shift destination item forward one placement
+ eval dest_$(( $n + 1 ))=\"\$dest_$n\"
+ n=$(( $n - 1 ))
+ done
+
+ # Place the element
+ if [ $i -eq 1 ]; then
+ local dest_1="$curitem"
+ else
+ local dest_$d="$curitem"
+ fi
+
+ i=$(( $i + 1 ))
+ done
+
+ # Print sorted results
+ d=1
+ while [ $d -le $nitems ]; do
+ eval echo \"\$dest_$d\"
+ d=$(( $d + 1 ))
+ done
+}
+
+#
+# wait_for_pids pid [pid ...]
+# spins until none of the pids exist
+#
+wait_for_pids()
+{
+ local _list _prefix _j
+
+ for _j in "$@"; do
+ if kill -0 $_j 2>/dev/null; then
+ _list="${_list}${_list:+ }$_j"
+ fi
+ done
+ _prefix=
+ while [ -n "$_list" ]; do
+ echo -n ${_prefix:-"Waiting for PIDS: "}$_list
+ _prefix=", "
+ _list=$(pwait -op $_list 2>/dev/null)
+ done
+ if [ -n "$_prefix" ]; then
+ echo "."
+ fi
+}
+
+#
+# get_pidfile_from_conf string file
+#
+# Takes a string to search for in the specified file.
+# Ignores lines with traditional comment characters.
+#
+# Example:
+#
+# if get_pidfile_from_conf string file; then
+# pidfile="$_pidfile_from_conf"
+# else
+# pidfile='appropriate default'
+# fi
+#
+get_pidfile_from_conf()
+{
+ if [ -z "$1" -o -z "$2" ]; then
+ err 3 "USAGE: get_pidfile_from_conf string file ($name)"
+ fi
+
+ local string file line
+
+ string="$1" ; file="$2"
+
+ if [ ! -s "$file" ]; then
+ err 3 "get_pidfile_from_conf: $file does not exist ($name)"
+ fi
+
+ while read line; do
+ case "$line" in
+ *[#\;]*${string}*) continue ;;
+ *${string}*) break ;;
+ esac
+ done < $file
+
+ if [ -n "$line" ]; then
+ line=${line#*/}
+ _pidfile_from_conf="/${line%%[\"\;]*}"
+ else
+ return 1
+ fi
+}
+
+#
+# check_startmsgs
+# If rc_quiet is set (usually as a result of using faststart at
+# boot time) check if rc_startmsgs is enabled.
+#
+check_startmsgs()
+{
+ if [ -n "$rc_quiet" ]; then
+ checkyesno rc_startmsgs
+ else
+ return 0
+ fi
+}
+
+#
+# startmsg
+# Preferred method to use when displaying start messages in lieu of echo.
+#
+startmsg()
+{
+ check_startmsgs && echo "$@"
+}
+
+#
+# run_rc_command argument
+# Search for argument in the list of supported commands, which is:
+# "start stop restart rcvar status poll ${extra_commands}"
+# If there's a match, run ${argument}_cmd or the default method
+# (see below).
+#
+# If argument has a given prefix, then change the operation as follows:
+# Prefix Operation
+# ------ ---------
+# fast Skip the pid check, and set rc_fast=yes, rc_quiet=yes
+# force Set ${rcvar} to YES, and set rc_force=yes
+# one Set ${rcvar} to YES
+# quiet Don't output some diagnostics, and set rc_quiet=yes
+#
+# The following globals are used:
+#
+# Name Needed Purpose
+# ---- ------ -------
+# name y Name of script.
+#
+# command n Full path to command.
+# Not needed if ${rc_arg}_cmd is set for
+# each keyword.
+#
+# command_args n Optional args/shell directives for command.
+#
+# command_interpreter n If not empty, command is interpreted, so
+# call check_{pidfile,process}() appropriately.
+#
+# desc n Description of script.
+#
+# extra_commands n List of extra commands supported.
+#
+# pidfile n If set, use check_pidfile $pidfile $command,
+# otherwise use check_process $command.
+# In either case, only check if $command is set.
+#
+# procname n Process name to check for instead of $command.
+#
+# rcvar n This is checked with checkyesno to determine
+# if the action should be run.
+#
+# ${name}_program n Full path to command.
+# Meant to be used in /etc/rc.conf to override
+# ${command}.
+#
+# ${name}_chroot n Directory to chroot to before running ${command}
+# Requires /usr to be mounted.
+#
+# ${name}_chdir n Directory to cd to before running ${command}
+# (if not using ${name}_chroot).
+#
+# ${name}_cpuset n A list of CPUs to run ${command} on.
+# Requires /usr to be mounted.
+#
+# ${name}_flags n Arguments to call ${command} with.
+# NOTE: $flags from the parent environment
+# can be used to override this.
+#
+# ${name}_env n Environment variables to run ${command} with.
+#
+# ${name}_env_file n File to source variables to run ${command} with.
+#
+# ${name}_fib n Routing table number to run ${command} with.
+#
+# ${name}_nice n Nice level to run ${command} at.
+#
+# ${name}_oomprotect n Don't kill ${command} when swap space is exhausted.
+#
+# ${name}_umask n The file creation mask to run ${command} with.
+#
+# ${name}_user n User to run ${command} as, using su(1) if not
+# using ${name}_chroot.
+# Requires /usr to be mounted.
+#
+# ${name}_group n Group to run chrooted ${command} as.
+# Requires /usr to be mounted.
+#
+# ${name}_groups n Comma separated list of supplementary groups
+# to run the chrooted ${command} with.
+# Requires /usr to be mounted.
+#
+# ${name}_prepend n Command added before ${command}.
+#
+# ${name}_setup n Command executed during start, restart and
+# reload before ${rc_arg}_precmd is run.
+#
+# ${name}_login_class n Login class to use, else "daemon".
+#
+# ${name}_limits n limits(1) to apply to ${command}.
+#
+# ${name}_offcmd n If set, run during start
+# if a service is not enabled.
+#
+# ${rc_arg}_cmd n If set, use this as the method when invoked;
+# Otherwise, use default command (see below)
+#
+# ${rc_arg}_precmd n If set, run just before performing the
+# ${rc_arg}_cmd method in the default
+# operation (i.e, after checking for required
+# bits and process (non)existence).
+# If this completes with a non-zero exit code,
+# don't run ${rc_arg}_cmd.
+#
+# ${rc_arg}_postcmd n If set, run just after performing the
+# ${rc_arg}_cmd method, if that method
+# returned a zero exit code.
+#
+# required_dirs n If set, check for the existence of the given
+# directories before running a (re)start command.
+#
+# required_files n If set, check for the readability of the given
+# files before running a (re)start command.
+#
+# required_modules n If set, ensure the given kernel modules are
+# loaded before running a (re)start command.
+# The check and possible loads are actually
+# done after start_precmd so that the modules
+# aren't loaded in vain, should the precmd
+# return a non-zero status to indicate a error.
+# If a word in the list looks like "foo:bar",
+# "foo" is the KLD file name and "bar" is the
+# module name. If a word looks like "foo~bar",
+# "foo" is the KLD file name and "bar" is a
+# egrep(1) pattern matching the module name.
+# Otherwise the module name is assumed to be
+# the same as the KLD file name, which is most
+# common. See load_kld().
+#
+# required_vars n If set, perform checkyesno on each of the
+# listed variables before running the default
+# (re)start command.
+#
+# Default behaviour for a given argument, if no override method is
+# provided:
+#
+# Argument Default behaviour
+# -------- -----------------
+# start if !running && checkyesno ${rcvar}
+# ${command}
+#
+# stop if ${pidfile}
+# rc_pid=$(check_pidfile $pidfile $command)
+# else
+# rc_pid=$(check_process $command)
+# kill $sig_stop $rc_pid
+# wait_for_pids $rc_pid
+# ($sig_stop defaults to TERM.)
+#
+# reload Similar to stop, except use $sig_reload instead,
+# and don't wait_for_pids.
+# $sig_reload defaults to HUP.
+# Note that `reload' isn't provided by default,
+# it should be enabled via $extra_commands.
+#
+# restart Run `stop' then `start'.
+#
+# status Show if ${command} is running, etc.
+#
+# poll Wait for ${command} to exit.
+#
+# rcvar Display what rc.conf variable is used (if any).
+#
+# enabled Return true if the service is enabled.
+#
+# describe Show the service's description
+#
+# extracommands Show the service's extra commands
+#
+# Variables available to methods, and after run_rc_command() has
+# completed:
+#
+# Variable Purpose
+# -------- -------
+# rc_arg Argument to command, after fast/force/one processing
+# performed
+#
+# rc_flags Flags to start the default command with.
+# Defaults to ${name}_flags, unless overridden
+# by $flags from the environment.
+# This variable may be changed by the precmd method.
+#
+# rc_service Path to the service being executed, in case the service
+# needs to re-invoke itself.
+#
+# rc_pid PID of command (if appropriate)
+#
+# rc_fast Not empty if "fast" was provided (q.v.)
+#
+# rc_force Not empty if "force" was provided (q.v.)
+#
+# rc_quiet Not empty if "quiet" was provided
+#
+#
+run_rc_command()
+{
+ _return=0
+ rc_arg=$1
+ if [ -z "$name" ]; then
+ err 3 'run_rc_command: $name is not set.'
+ fi
+
+ DebugOn rc:all rc:all:$rc_arg rc:$name rc:$name:$rc_arg $name:$rc_arg
+
+ # Don't repeat the first argument when passing additional command-
+ # line arguments to the command subroutines.
+ #
+ shift 1
+ rc_extra_args="$*"
+
+ _rc_prefix=
+ case "$rc_arg" in
+ fast*) # "fast" prefix; don't check pid
+ rc_arg=${rc_arg#fast}
+ rc_fast=yes
+ rc_quiet=yes
+ ;;
+ force*) # "force" prefix; always run
+ rc_force=yes
+ _rc_prefix=force
+ rc_arg=${rc_arg#${_rc_prefix}}
+ if [ -n "${rcvar}" ]; then
+ eval ${rcvar}=YES
+ fi
+ ;;
+ one*) # "one" prefix; set ${rcvar}=yes
+ _rc_prefix=one
+ rc_arg=${rc_arg#${_rc_prefix}}
+ if [ -n "${rcvar}" ]; then
+ eval ${rcvar}=YES
+ fi
+ ;;
+ quiet*) # "quiet" prefix; omit some messages
+ _rc_prefix=quiet
+ rc_arg=${rc_arg#${_rc_prefix}}
+ rc_quiet=yes
+ ;;
+ esac
+
+ eval _override_command=\$${name}_program
+ command=${_override_command:-$command}
+
+ _keywords="start stop restart rcvar enable disable delete enabled describe extracommands $extra_commands"
+ rc_pid=
+ _pidcmd=
+ _procname=${procname:-${command}}
+
+ eval _cpuset=\$${name}_cpuset
+
+ # Loose validation of the configured cpuset; just make sure it starts
+ # with a number. There have also been cases in the past where a hyphen
+ # in a service name has caused eval errors, which trickle down into
+ # various variables; don't let a situation like that break a bunch of
+ # services just because of cpuset(1).
+ case "$_cpuset" in
+ [0-9]*) ;;
+ *) _cpuset="" ;;
+ esac
+
+ _cpusetcmd=
+ if [ -n "$_cpuset" ]; then
+ _cpusetcmd="$CPUSET -l $_cpuset"
+ fi
+
+ # If a specific jail has a specific svcj request, honor it (YES/NO).
+ # If not (variable empty), evaluate the global svcj catch-all.
+ # A global YES can be overriden by a specific NO, and a global NO is overriden
+ # by a specific YES.
+ eval _svcj=\$${name}_svcj
+ if [ -z "$_svcj" ]; then
+ _svcj=${svcj_all_enable}
+ if [ -z "$_svcj" ]; then
+ eval ${name}_svcj=NO
+ fi
+ fi
+
+ # setup pid check command
+ if [ -n "$_procname" ]; then
+ if [ -n "$pidfile" ]; then
+ _pidcmd='rc_pid=$(check_pidfile '"$pidfile $_procname $command_interpreter"')'
+ else
+ _pidcmd='rc_pid=$(check_process '"$_procname $command_interpreter"')'
+ fi
+ _keywords="${_keywords} status poll"
+ else
+ if [ ! -z "${status_cmd}" ]
+ then
+ _keywords="${_keywords} status"
+ fi
+ fi
+
+ if [ -z "$rc_arg" ]; then
+ rc_usage $_keywords
+ fi
+
+ if [ "$rc_arg" = "enabled" ] ; then
+ checkyesno ${rcvar}
+ return $?
+ fi
+
+ if [ -n "$flags" ]; then # allow override from environment
+ rc_flags=$flags
+ else
+ eval rc_flags=\$${name}_flags
+ fi
+ eval _chdir=\$${name}_chdir _chroot=\$${name}_chroot \
+ _nice=\$${name}_nice _user=\$${name}_user \
+ _group=\$${name}_group _groups=\$${name}_groups \
+ _fib=\$${name}_fib _env=\$${name}_env \
+ _prepend=\$${name}_prepend _login_class=\${${name}_login_class:-daemon} \
+ _limits=\$${name}_limits _oomprotect=\$${name}_oomprotect \
+ _setup=\$${name}_setup _env_file=\$${name}_env_file \
+ _umask=\$${name}_umask _svcj_options=\$${name}_svcj_options \
+ _svcj_ipaddrs=\$${name}_svcj_ipaddrs
+
+ if [ -n "$_env_file" ] && [ -r "${_env_file}" ]; then # load env from file
+ set -a
+ . $_env_file
+ set +a
+ fi
+
+ if [ -n "$_user" ]; then # unset $_user if running as that user
+ if [ "$_user" = "$(eval $IDCMD)" ]; then
+ unset _user
+ fi
+ fi
+
+ _svcj_ip4_addrs=""
+ _svcj_ip6_addrs=""
+ _svcj_cmd_options=""
+
+ if [ -n "$_svcj_ipaddrs" ]; then
+ _svcj_ip="new"
+
+ for addr in $_svcj_ipaddrs; do
+ case $addr in
+ *:*) _svcj_ip6_addrs="$addr,${_svcj_ip6_addrs}" ;;
+ *) _svcj_ip4_addrs="$addr,${_svcj_ip4_addrs}" ;;
+ esac
+ done
+ else
+ _svcj_ip="inherit"
+ fi
+
+ if check_kern_features inet; then
+ _svcj_ip4="ip4=${_svcj_ip}"
+ if [ -n "$_svcj_ip4_addrs" ]; then
+ _svcj_cmd_options="ip4.addr=${_svcj_ip4_addrs%*,} ${_svcj_cmd_options}"
+ fi
+ else
+ if [ -n "$_svcj_ip4_addrs" ]; then
+ warn "$rc_service: ${name}_svcj_ipaddrs contains at least one IPv4 address, but IPv4 is not enabled in the kernel; IPv4 addresses will be ignored."
+ fi
+ fi
+
+ if check_kern_features inet6; then
+ _svcj_ip6="ip6=${_svcj_ip}"
+ if [ -n "$_svcj_ip6_addrs" ]; then
+ _svcj_cmd_options="ip6.addr=${_svcj_ip6_addrs%*,} ${_svcj_cmd_options}"
+ fi
+ else
+ if [ -n "$_svcj_ip6_addrs" ]; then
+ warn "$rc_service: ${name}_svcj_ipaddrs contains at least one IPv6 address, but IPv6 is not enabled in the kernel; IPv6 addresses will be ignored."
+ fi
+ fi
+
+ if [ -n "$_svcj_options" ]; then # translate service jail options
+ _svcj_sysvipc_x=0
+ for _svcj_option in $_svcj_options; do
+ case "$_svcj_option" in
+ mlock)
+ _svcj_cmd_options="allow.mlock ${_svcj_cmd_options}"
+ ;;
+ netv4)
+ _svcj_cmd_options="${_svcj_ip4} allow.reserved_ports ${_svcj_cmd_options}"
+ ;;
+ netv6)
+ _svcj_cmd_options="${_svcj_ip6} allow.reserved_ports ${_svcj_cmd_options}"
+ ;;
+ net_basic)
+ _svcj_cmd_options="${_svcj_ip4} ${_svcj_ip6} allow.reserved_ports ${_svcj_cmd_options}"
+ ;;
+ net_raw)
+ _svcj_cmd_options="allow.raw_sockets ${_svcj_cmd_options}"
+ ;;
+ net_all)
+ _svcj_cmd_options="allow.socket_af allow.raw_sockets allow.reserved_ports ${_svcj_ip4} ${_svcj_ip6} ${_svcj_cmd_options}"
+ ;;
+ nfsd)
+ _svcj_cmd_options="allow.nfsd enforce_statfs=1 ${_svcj_cmd_options}"
+ ;;
+ routing)
+ _svcj_cmd_options="allow.routing ${_svcj_cmd_options}"
+ ;;
+ settime)
+ _svcj_cmd_options="allow.settime ${_svcj_cmd_options}"
+ ;;
+ sysvipc)
+ _svcj_sysvipc_x=$((${_svcj_sysvipc_x} + 1))
+ _svcj_cmd_options="sysvmsg=inherit sysvsem=inherit sysvshm=inherit ${_svcj_cmd_options}"
+ ;;
+ sysvipcnew)
+ _svcj_sysvipc_x=$((${_svcj_sysvipc_x} + 1))
+ _svcj_cmd_options="sysvmsg=new sysvsem=new sysvshm=new ${_svcj_cmd_options}"
+ ;;
+ vmm)
+ _svcj_cmd_options="allow.vmm ${_svcj_cmd_options}"
+ ;;
+ *)
+ echo ${name}: unknown service jail option: $_svcj_option
+ ;;
+ esac
+ done
+ if [ ${_svcj_sysvipc_x} -gt 1 ]; then
+ echo -n "ERROR: more than one sysvipc option is "
+ echo "specified in ${name}_svcj_options: $_svcj_options"
+ return 1
+ fi
+ fi
+
+ [ -z "$autoboot" ] && eval $_pidcmd # determine the pid if necessary
+
+ for _elem in $_keywords; do
+ if [ "$_elem" != "$rc_arg" ]; then
+ continue
+ fi
+ # if ${rcvar} is set, $1 is not "rcvar", "describe",
+ # "enable", "delete" or "status", and ${rc_pid} is
+ # not set, run:
+ # checkyesno ${rcvar}
+ # and return if that failed
+ #
+ if [ -n "${rcvar}" -a "$rc_arg" != "rcvar" -a "$rc_arg" != "stop" \
+ -a "$rc_arg" != "delete" -a "$rc_arg" != "enable" \
+ -a "$rc_arg" != "describe" -a "$rc_arg" != "status" ] ||
+ [ -n "${rcvar}" -a "$rc_arg" = "stop" -a -z "${rc_pid}" ]; then
+ if ! checkyesno ${rcvar}; then
+ [ "$rc_arg" = "start" ] && _run_rc_offcmd
+ if [ -z "${rc_quiet}" ]; then
+ echo -n "Cannot '${rc_arg}' $name. Set ${rcvar} to "
+ echo -n "YES in /etc/rc.conf or use 'one${rc_arg}' "
+ echo "instead of '${rc_arg}'."
+ fi
+ return 0
+ fi
+ fi
+
+ if [ $rc_arg = "start" -a -z "$rc_fast" -a -n "$rc_pid" ]; then
+ if [ -z "$rc_quiet" ]; then
+ echo 1>&2 "${name} already running? " \
+ "(pid=$rc_pid)."
+ fi
+ return 1
+ fi
+
+ # if there's a custom ${XXX_cmd},
+ # run that instead of the default
+ #
+ eval _cmd=\$${rc_arg}_cmd \
+ _precmd=\$${rc_arg}_precmd \
+ _postcmd=\$${rc_arg}_postcmd
+
+ if [ -n "$_cmd" ]; then
+ if [ "$_cmd" != : ]; then
+ rc_trace 1 "$_cmd"
+ fi
+ if [ -n "$_env" ]; then
+ eval "export -- $_env"
+ fi
+
+ if [ "${_rc_svcj}" != jailing ]; then
+ # service can redefine all so
+ # check for valid setup target
+ if [ "$rc_arg" = 'start' -o \
+ "$rc_arg" = 'restart' -o \
+ "$rc_arg" = 'reload' ]; then
+ _run_rc_setup || \
+ warn "failed to setup ${name}"
+ fi
+ _run_rc_precmd || return 1
+ fi
+ if ! checkyesno ${name}_svcj; then
+ _run_rc_doit "$_cpusetcmd $_cmd $rc_extra_args" || return 1
+ else
+ case "$rc_arg" in
+ start)
+ if [ "${_rc_svcj}" != jailing ]; then
+ _return=1
+ _do_jailing=1
+
+ if check_jail jailed; then
+ if [ $(${SYSCTL_N} security.jail.children.max) -eq 0 ]; then
+ echo ERROR: jail parameter children.max is set to 0, can not create a new service jail.
+ _do_jailing=0
+ else
+ _free_jails=$(($(${SYSCTL_N} security.jail.children.max) - $(${SYSCTL_N} security.jail.children.cur)))
+ if [ ${_free_jails} -eq 0 ]; then
+ echo ERROR: max number of jail children reached, can not create a new service jail.
+ _do_jailing=0
+
+ fi
+ fi
+ fi
+ if [ ${_do_jailing} -eq 1 ]; then
+ $JAIL_CMD -c $_svcj_generic_params $_svcj_cmd_options \
+ exec.start="${SERVICE} -E _rc_svcj=jailing ${name} ${_rc_prefix}start $rc_extra_args" \
+ exec.stop="${SERVICE} -E _rc_svcj=jailing ${name} ${_rc_prefix}stop $rc_extra_args" \
+ exec.consolelog="/var/log/svcj_${name}_console.log" \
+ name=svcj-${name} && _return=0
+ fi
+ else
+ _run_rc_doit "$_cpusetcmd $_cmd $rc_extra_args" || _return=1
+ fi
+ ;;
+ stop)
+ if [ "${_rc_svcj}" != jailing ]; then
+ $SERVICE -E _rc_svcj=jailing -j svcj-${name} ${name} ${_rc_prefix}stop $rc_extra_args || _return=1
+ $JAIL_CMD -r svcj-${name} 2>/dev/null
+ else
+ _run_rc_doit "$_cpusetcmd $_cmd $rc_extra_args" || _return=1
+ fi
+ ;;
+ restart|status) ;; # no special case needed for svcj or handled somewhere else
+ *)
+ eval _rc_svcj_extra_cmd=\$${name}_${rc_arg}_svcj_enable
+ : ${_rc_svcj_extra_cmd:=NO}
+ if checkyesno _rc_svcj_extra_cmd && [ "${_rc_svcj}" != jailing ]; then
+ $SERVICE -v -E _rc_svcj=jailing -j svcj-${name} ${name} ${_rc_prefix}${rc_arg} $rc_extra_args || _return=1
+ else
+ _run_rc_doit "$_cpusetcmd $_cmd $rc_extra_args" || _return=1
+ fi
+ ;;
+ esac
+ fi
+ if [ "${_rc_svcj}" != jailing ]; then
+ _run_rc_postcmd
+ fi
+ return $_return
+ fi
+
+ case "$rc_arg" in # default operations...
+
+ describe)
+ if [ -n "$desc" ]; then
+ echo "$desc"
+ fi
+ ;;
+
+ extracommands)
+ echo "$extra_commands"
+ ;;
+
+ enable)
+ _out=$(write_rcvar "$rcvar" "YES") &&
+ echo "$name enabled in $_out"
+ ;;
+
+ disable)
+ _out=$(write_rcvar "$rcvar" "NO") &&
+ echo "$name disabled in $_out"
+ ;;
+
+ delete)
+ delete_rcvar "$rcvar"
+ ;;
+
+ status)
+ _run_rc_precmd || return 1
+ if [ -n "$rc_pid" ]; then
+ echo "${name} is running as pid $rc_pid."
+ else
+ echo "${name} is not running."
+ return 1
+ fi
+ _run_rc_postcmd
+ ;;
+
+ start)
+ if [ ! -x "${_chroot}${_chroot:+/}${command}" ]; then
+ warn "run_rc_command: cannot run $command"
+ return 1
+ fi
+
+ if [ "${_rc_svcj}" != jailing ]; then
+ _run_rc_setup || warn "failed to setup ${name}"
+
+ if ! _run_rc_precmd; then
+ warn "failed precmd routine for ${name}"
+ return 1
+ fi
+ fi
+
+ if checkyesno ${name}_svcj; then
+ if [ "${_rc_svcj}" != jailing ]; then
+ if check_jail jailed; then
+ if [ $(${SYSCTL_N} security.jail.children.max) -eq 0 ]; then
+ echo ERROR: jail parameter children.max is set to 0, can not create a new service jail.
+ return 1
+ else
+ _free_jails=$(($(${SYSCTL_N} security.jail.children.max) - $(${SYSCTL_N} security.jail.children.cur)))
+ if [ ${_free_jails} -eq 0 ]; then
+ echo ERROR: max number of jail children reached, can not create a new service jail.
+ return 1
+ fi
+ fi
+ fi
+ $JAIL_CMD -c $_svcj_generic_params $_svcj_cmd_options\
+ exec.start="${SERVICE} -E _rc_svcj=jailing ${name} ${_rc_prefix}start $rc_extra_args" \
+ exec.stop="${SERVICE} -E _rc_svcj=jailing ${name} ${_rc_prefix}stop $rc_extra_args" \
+ exec.consolelog="/var/log/svcj_${name}_console.log" \
+ name=svcj-${name} || return 1
+ fi
+ fi
+
+ # setup the full command to run
+ #
+ startmsg "Starting ${name}."
+ if [ -n "$_chroot" ]; then
+ _cd=
+ _doit="\
+${_nice:+nice -n $_nice }\
+$_cpusetcmd \
+${_fib:+setfib -F $_fib }\
+${_env:+env $_env }\
+chroot ${_user:+-u $_user }${_group:+-g $_group }${_groups:+-G $_groups }\
+$_chroot $command $rc_flags $command_args"
+ else
+ _cd="${_chdir:+cd $_chdir && }"
+ _doit="\
+${_fib:+setfib -F $_fib }\
+${_env:+env $_env }\
+$_cpusetcmd $command $rc_flags $command_args"
+ if [ -n "$_user" ]; then
+ _doit="su -m $_user -c 'sh -c \"$_doit\"'"
+ fi
+ if [ -n "$_nice" ]; then
+ if [ -z "$_user" ]; then
+ _doit="sh -c \"$_doit\""
+ fi
+ _doit="nice -n $_nice $_doit"
+ fi
+ if [ -n "$_prepend" ]; then
+ _doit="$_prepend $_doit"
+ fi
+ fi
+
+ # Prepend default limits
+ _doit="$_cd limits -C $_login_class $_limits $_doit"
+
+ local _really_run_it=true
+ if checkyesno ${name}_svcj; then
+ if [ "${_rc_svcj}" != jailing ]; then
+ _really_run_it=false
+ fi
+ fi
+
+ if [ "$_really_run_it" = true ]; then
+ # run the full command
+ #
+ if ! _run_rc_doit "$_doit"; then
+ warn "failed to start ${name}"
+ return 1
+ fi
+ fi
+
+ if [ "${_rc_svcj}" != jailing ]; then
+ # finally, run postcmd
+ #
+ _run_rc_postcmd
+ fi
+ ;;
+
+ stop)
+ if [ -z "$rc_pid" ]; then
+ [ -n "$rc_fast" ] && return 0
+ _run_rc_notrunning
+ return 1
+ fi
+
+ _run_rc_precmd || return 1
+
+ # send the signal to stop
+ #
+ echo "Stopping ${name}."
+ _doit=$(_run_rc_killcmd "${sig_stop:-TERM}")
+ _run_rc_doit "$_doit" || return 1
+
+ # wait for the command to exit,
+ # and run postcmd.
+ wait_for_pids $rc_pid
+
+ if checkyesno ${name}_svcj; then
+ # remove service jail
+ $JAIL_CMD -r svcj-${name} 2>/dev/null
+ fi
+
+ _run_rc_postcmd
+ ;;
+
+ reload)
+ if [ -z "$rc_pid" ]; then
+ _run_rc_notrunning
+ return 1
+ fi
+
+ _run_rc_setup || warn "failed to setup ${name}"
+
+ _run_rc_precmd || return 1
+
+ _doit=$(_run_rc_killcmd "${sig_reload:-HUP}")
+ _run_rc_doit "$_doit" || return 1
+
+ _run_rc_postcmd
+ ;;
+
+ restart)
+ _run_rc_setup || warn "failed to setup ${name}"
+
+ # prevent restart being called more
+ # than once by any given script
+ #
+ if ${_rc_restart_done:-false}; then
+ return 0
+ fi
+ _rc_restart_done=true
+
+ _run_rc_precmd || return 1
+
+ # run those in a subshell to keep global variables
+ ( run_rc_command ${_rc_prefix}stop $rc_extra_args )
+ ( run_rc_command ${_rc_prefix}start $rc_extra_args )
+ _return=$?
+ [ $_return -ne 0 ] && [ -z "$rc_force" ] && return 1
+
+ _run_rc_postcmd
+ ;;
+
+ poll)
+ _run_rc_precmd || return 1
+ if [ -n "$rc_pid" ]; then
+ wait_for_pids $rc_pid
+ fi
+ _run_rc_postcmd
+ ;;
+
+ rcvar)
+ echo -n "# $name"
+ if [ -n "$desc" ]; then
+ echo " : $desc"
+ else
+ echo ""
+ fi
+ echo "#"
+ # Get unique vars in $rcvar $rcvars
+ for _v in $rcvar $rcvars; do
+ case $v in
+ $_v\ *|\ *$_v|*\ $_v\ *) ;;
+ *) v="${v# } $_v" ;;
+ esac
+ done
+
+ # Display variables.
+ for _v in $v; do
+ if [ -z "$_v" ]; then
+ continue
+ fi
+
+ eval _desc=\$${_v}_desc
+ eval _defval=\$${_v}_defval
+ _h="-"
+
+ eval echo \"$_v=\\\"\$$_v\\\"\"
+ # decode multiple lines of _desc
+ while [ -n "$_desc" ]; do
+ case $_desc in
+ *^^*)
+ echo "# $_h ${_desc%%^^*}"
+ _desc=${_desc#*^^}
+ _h=" "
+ ;;
+ *)
+ echo "# $_h ${_desc}"
+ break
+ ;;
+ esac
+ done
+ echo "# (default: \"$_defval\")"
+ done
+ echo ""
+ ;;
+
+ *)
+ rc_usage $_keywords
+ ;;
+
+ esac
+
+ # Apply protect(1) to the PID if ${name}_oomprotect is set.
+ case "$rc_arg" in
+ start)
+ # We cannot use protect(1) inside jails.
+ if [ -n "$_oomprotect" ] && [ -f "${PROTECT}" ] &&
+ ! check_jail jailed; then
+ [ -z "${rc_pid}" ] && eval $_pidcmd
+ case $_oomprotect in
+ [Aa][Ll][Ll])
+ ${PROTECT} -d -i -p ${rc_pid}
+ ;;
+ [Yy][Ee][Ss])
+ ${PROTECT} -p ${rc_pid}
+ ;;
+ esac
+ fi
+ ;;
+ esac
+
+ return $_return
+ done
+
+ echo 1>&2 "$0: unknown directive '$rc_arg'."
+ rc_usage $_keywords
+ # not reached
+}
+
+#
+# Helper functions for run_rc_command: common code.
+# They use such global variables besides the exported rc_* ones:
+#
+# name R/W
+# ------------------
+# _offcmd R
+# _precmd R
+# _postcmd R
+# _return W
+# _setup R
+#
+_run_rc_offcmd()
+{
+ eval _offcmd=\$${name}_offcmd
+ if [ -n "$_offcmd" ]; then
+ if [ -n "$_env" ]; then
+ eval "export -- $_env"
+ fi
+ debug "run_rc_command: ${name}_offcmd: $_offcmd $rc_extra_args"
+ eval "$_offcmd $rc_extra_args"
+ _return=$?
+ fi
+ return 0
+}
+
+_run_rc_precmd()
+{
+ check_required_before "$rc_arg" || return 1
+
+ if [ -n "$_precmd" ]; then
+ debug "run_rc_command: ${rc_arg}_precmd: $_precmd $rc_extra_args"
+ eval "$_precmd $rc_extra_args"
+ _return=$?
+
+ # If precmd failed and force isn't set, request exit.
+ if [ $_return -ne 0 ] && [ -z "$rc_force" ]; then
+ return 1
+ fi
+ fi
+
+ check_required_after "$rc_arg" || return 1
+
+ return 0
+}
+
+_run_rc_postcmd()
+{
+ if [ -n "$_postcmd" ]; then
+ debug "run_rc_command: ${rc_arg}_postcmd: $_postcmd $rc_extra_args"
+ eval "$_postcmd $rc_extra_args"
+ _return=$?
+ fi
+ return 0
+}
+
+_run_rc_setup()
+{
+ # prevent multiple execution on restart => stop/start split
+ if ! ${_rc_restart_done:-false} && [ -n "$_setup" ]; then
+ debug "run_rc_command: ${rc_arg}_setup: $_setup"
+ eval "$_setup"
+ _return=$?
+ if [ $_return -ne 0 ]; then
+ return 1
+ fi
+ fi
+ return 0
+}
+
+_run_rc_doit()
+{
+ local _m
+
+ debug "run_rc_command: doit: $*"
+ _m=$(umask)
+ ${_umask:+umask ${_umask}}
+ eval "$@"
+ _return=$?
+ umask ${_m}
+
+ # If command failed and force isn't set, request exit.
+ if [ $_return -ne 0 ] && [ -z "$rc_force" ]; then
+ return 1
+ fi
+
+ return 0
+}
+
+_run_rc_notrunning()
+{
+ local _pidmsg
+
+ if [ -n "$pidfile" ]; then
+ _pidmsg=" (check $pidfile)."
+ else
+ _pidmsg=
+ fi
+ echo 1>&2 "${name} not running?${_pidmsg}"
+}
+
+_run_rc_killcmd()
+{
+ local _cmd
+
+ _cmd="kill -$1 $rc_pid"
+ if [ -n "$_user" ]; then
+ _cmd="su -m ${_user} -c 'sh -c \"${_cmd}\"'"
+ fi
+ echo "$_cmd"
+}
+
+#
+# run_rc_script file arg
+# Start the script `file' with `arg', and correctly handle the
+# return value from the script.
+# If `file' ends with `.sh' and lives in /etc/rc.d, ignore it as it's
+# an old-style startup file.
+# If `file' appears to be a backup or scratch file, ignore it.
+# Otherwise if it is executable run as a child process.
+#
+run_rc_script()
+{
+ _file=$1
+ _arg=$2
+ if [ -z "$_file" -o -z "$_arg" ]; then
+ err 3 'USAGE: run_rc_script file arg'
+ fi
+
+ unset name command command_args command_interpreter \
+ extra_commands pidfile procname \
+ rcvar rcvars rcvars_obsolete required_dirs required_files \
+ required_vars
+ eval unset ${_arg}_cmd ${_arg}_precmd ${_arg}_postcmd
+
+ rc_trace 0 "$_file $_arg"
+ # don't use it if we don't trust it
+ is_verified $_file || return
+
+ rc_service="$_file"
+ case "$_file" in
+ /etc/rc.d/*.sh) # no longer allowed in the base
+ warn "Ignoring old-style startup script $_file"
+ ;;
+ *[~#]|*.OLD|*.bak|*.orig|*,v) # scratch file; skip
+ warn "Ignoring scratch file $_file"
+ ;;
+ *) # run in subshell
+ if [ -x $_file ]; then
+ DebugOn $_file $_file:$_arg rc:${_file##*/} rc:${_file##*/}:$_arg ${_file##*/} ${_file##*/}:$_arg
+
+ if [ -n "$rc_boottrace" ]; then
+ boottrace_fn "$_file" "$_arg"
+ else
+ ( trap "echo Script $_file interrupted >&2 ; kill -QUIT $$" 3
+ trap "echo Script $_file interrupted >&2 ; exit 1" 2
+ trap "echo Script $_file running >&2" 29
+ set $_arg; . $_file )
+ fi
+ DebugOff rc=$? $_file $_file:$_arg rc:${_file##*/} rc:${_file##*/}:$_arg ${_file##*/} ${_file##*/}:$_arg
+ fi
+ ;;
+ esac
+}
+
+#
+# run_rc_scripts [options] file [...]
+#
+# Call `run_rc_script' for each "file" unless already listed in
+# $_rc_elem_done.
+#
+# Options:
+#
+# --arg "arg"
+# Pass "arg" to `run_rc_script' default is $_boot.
+#
+# --break "marker"
+# If any "file" matches "marker" stop processing.
+#
+_rc_elem_done=
+run_rc_scripts()
+{
+ local _arg=${_boot}
+ local _rc_elem
+ local _rc_breaks=
+
+ while :; do
+ case "$1" in
+ --arg)
+ _arg="$2"
+ shift 2
+ ;;
+ --break)
+ _rc_breaks="$_rc_breaks $2"
+ shift 2
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+ for _rc_elem in "$@"; do
+ : _rc_elem=$_rc_elem
+ case " $_rc_elem_done " in
+ *" $_rc_elem "*)
+ continue
+ ;;
+ esac
+ run_rc_script ${_rc_elem} ${_arg}
+ _rc_elem_done="$_rc_elem_done $_rc_elem"
+ case " $_rc_breaks " in
+ *" ${_rc_elem##*/} "*)
+ break
+ ;;
+ esac
+ done
+}
+
+boottrace_fn()
+{
+ local _file _arg
+ _file=$1
+ _arg=$2
+
+ _boot="${_boot}" rc_fast="${rc_fast}" autoboot="${autoboot}" \
+ $boottrace_cmd "$_file" "$_arg"
+}
+
+#
+# load_rc_config [service]
+# Source in the configuration file(s) for a given service.
+# If no service is specified, only the global configuration
+# file(s) will be loaded.
+#
+load_rc_config()
+{
+ local _name _rcvar_val _var _defval _v _msg _new _d _dot
+ _name=$1
+ _dot=${load_rc_config_reader:-dot}
+
+ case "$_dot" in
+ dot|[sv]dot)
+ ;;
+ *) warn "Ignoring invalid load_rc_config_reader"
+ _dot=dot
+ ;;
+ esac
+ case "$1" in
+ -s|--safe)
+ _dot=sdot
+ _name=$2
+ shift
+ ;;
+ -v|--verify)
+ _dot=vdot
+ _name=$2
+ shift
+ ;;
+ esac
+
+ DebugOn rc:$_name $_name
+
+ if ${_rc_conf_loaded:-false}; then
+ :
+ else
+ if [ -r /etc/defaults/rc.conf ]; then
+ debug "Sourcing /etc/defaults/rc.conf"
+ $_dot /etc/defaults/rc.conf
+ source_rc_confs
+ elif [ -r /etc/rc.conf ]; then
+ debug "Sourcing /etc/rc.conf (/etc/defaults/rc.conf doesn't exist)."
+ $_dot /etc/rc.conf
+ fi
+ _rc_conf_loaded=true
+ fi
+
+ # If a service name was specified, attempt to load
+ # service-specific configuration
+ if [ -n "$_name" ] ; then
+ _loaded_services="${_loaded_services} ${_name}"
+ for _d in /etc ${local_startup}; do
+ _d=${_d%/rc.d}
+ if [ -f ${_d}/rc.conf.d/"$_name" ]; then
+ debug "Sourcing ${_d}/rc.conf.d/$_name"
+ $_dot ${_d}/rc.conf.d/"$_name"
+ elif [ -d ${_d}/rc.conf.d/"$_name" ] ; then
+ local _rc
+ for _rc in ${_d}/rc.conf.d/"$_name"/* ; do
+ if [ -f "$_rc" ] ; then
+ debug "Sourcing $_rc"
+ $_dot "$_rc"
+ fi
+ done
+ fi
+ done
+ fi
+
+ # Set defaults if defined.
+ for _var in $rcvar $rcvars; do
+ eval _defval=\$${_var}_defval
+ if [ -n "$_defval" ]; then
+ eval : \${$_var:=\$${_var}_defval}
+ fi
+ done
+
+ # check obsolete rc.conf variables
+ for _var in $rcvars_obsolete; do
+ eval _v=\$$_var
+ eval _msg=\$${_var}_obsolete_msg
+ eval _new=\$${_var}_newvar
+ case $_v in
+ "")
+ ;;
+ *)
+ if [ -z "$_new" ]; then
+ _msg="Ignored."
+ else
+ eval $_new=\"\$$_var\"
+ if [ -z "$_msg" ]; then
+ _msg="Use \$$_new instead."
+ fi
+ fi
+ warn "\$$_var is obsolete. $_msg"
+ ;;
+ esac
+ done
+}
+
+#
+# load_rc_config_var name var
+# Read the rc.conf(5) var for name and set in the
+# current shell, using load_rc_config in a subshell to prevent
+# unwanted side effects from other variable assignments.
+#
+load_rc_config_var()
+{
+ if [ $# -ne 2 ]; then
+ err 3 'USAGE: load_rc_config_var name var'
+ fi
+ eval $(eval '(
+ load_rc_config '$1' >/dev/null;
+ if [ -n "${'$2'}" -o "${'$2'-UNSET}" != "UNSET" ]; then
+ echo '$2'=\'\''${'$2'}\'\'';
+ fi
+ )' )
+}
+
+#
+# rc_usage commands
+# Print a usage string for $0, with `commands' being a list of
+# valid commands.
+#
+rc_usage()
+{
+ echo -n 1>&2 "Usage: $0 [fast|force|one|quiet]("
+
+ _sep=
+ for _elem; do
+ echo -n 1>&2 "$_sep$_elem"
+ _sep="|"
+ done
+ echo 1>&2 ")"
+ exit 1
+}
+
+#
+# err exitval message
+# Display message to stderr and log to the syslog, and exit with exitval.
+#
+err()
+{
+ exitval=$1
+ shift
+
+ if [ -x /usr/bin/logger ]; then
+ logger "$0: ERROR: $*"
+ fi
+ echo 1>&2 "$0: ERROR: $*"
+ exit $exitval
+}
+
+#
+# warn message
+# Display message to stderr and log to the syslog.
+#
+warn()
+{
+ if [ -x /usr/bin/logger ]; then
+ logger "$0: WARNING: $*"
+ fi
+ echo 1>&2 "$0: WARNING: $*"
+}
+
+#
+# info message
+# Display informational message to stdout and log to syslog.
+#
+info()
+{
+ case ${rc_info} in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+ if [ -x /usr/bin/logger ]; then
+ logger "$0: INFO: $*"
+ fi
+ echo "$0: INFO: $*"
+ ;;
+ esac
+}
+
+#
+# debug message
+# If debugging is enabled in rc.conf output message to stderr.
+# BEWARE that you don't call any subroutine that itself calls this
+# function.
+#
+debug()
+{
+ case ${rc_debug} in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+ if [ -x /usr/bin/logger ]; then
+ logger "$0: DEBUG: $*"
+ fi
+ echo 1>&2 "$0: DEBUG: $*"
+ ;;
+ esac
+}
+
+#
+# backup_file action file cur backup
+# Make a backup copy of `file' into `cur', and save the previous
+# version of `cur' as `backup'.
+#
+# The `action' keyword can be one of the following:
+#
+# add `file' is now being backed up (and is possibly
+# being reentered into the backups system). `cur'
+# is created.
+#
+# update `file' has changed and needs to be backed up.
+# If `cur' exists, it is copied to `back'
+# and then `file' is copied to `cur'.
+#
+# remove `file' is no longer being tracked by the backups
+# system. `cur' is moved `back'.
+#
+#
+backup_file()
+{
+ _action=$1
+ _file=$2
+ _cur=$3
+ _back=$4
+
+ case $_action in
+ add|update)
+ if [ -f $_cur ]; then
+ cp -p $_cur $_back
+ fi
+ cp -p $_file $_cur
+ chown root:wheel $_cur
+ ;;
+ remove)
+ mv -f $_cur $_back
+ ;;
+ esac
+}
+
+# make_symlink src link
+# Make a symbolic link 'link' to src from basedir. If the
+# directory in which link is to be created does not exist
+# a warning will be displayed and an error will be returned.
+# Returns 0 on success, 1 otherwise.
+#
+make_symlink()
+{
+ local src link linkdir _me
+ src="$1"
+ link="$2"
+ linkdir="`dirname $link`"
+ _me="make_symlink()"
+
+ if [ -z "$src" -o -z "$link" ]; then
+ warn "$_me: requires two arguments."
+ return 1
+ fi
+ if [ ! -d "$linkdir" ]; then
+ warn "$_me: the directory $linkdir does not exist."
+ return 1
+ fi
+ if ! ln -sf $src $link; then
+ warn "$_me: unable to make a symbolic link from $link to $src"
+ return 1
+ fi
+ return 0
+}
+
+# devfs_rulesets_from_file file
+# Reads a set of devfs commands from file, and creates
+# the specified rulesets with their rules. Returns non-zero
+# if there was an error.
+#
+devfs_rulesets_from_file()
+{
+ local file _err _me _opts
+ file="$1"
+ _me="devfs_rulesets_from_file"
+ _err=0
+
+ if [ -z "$file" ]; then
+ warn "$_me: you must specify a file"
+ return 1
+ fi
+ if [ ! -e "$file" ]; then
+ debug "$_me: no such file ($file)"
+ return 0
+ fi
+
+ # Disable globbing so that the rule patterns are not expanded
+ # by accident with matching filesystem entries.
+ _opts=$-; set -f
+
+ debug "reading rulesets from file ($file)"
+ { while read line
+ do
+ case $line in
+ \#*)
+ continue
+ ;;
+ \[*\]*)
+ rulenum=`expr "$line" : "\[.*=\([0-9]*\)\]"`
+ if [ -z "$rulenum" ]; then
+ warn "$_me: cannot extract rule number ($line)"
+ _err=1
+ break
+ fi
+ rulename=`expr "$line" : "\[\(.*\)=[0-9]*\]"`
+ if [ -z "$rulename" ]; then
+ warn "$_me: cannot extract rule name ($line)"
+ _err=1
+ break;
+ fi
+ eval $rulename=\$rulenum
+ debug "found ruleset: $rulename=$rulenum"
+ if ! /sbin/devfs rule -s $rulenum delset; then
+ _err=1
+ break
+ fi
+ ;;
+ *)
+ rulecmd="${line%%"\#*"}"
+ # evaluate the command incase it includes
+ # other rules
+ if [ -n "$rulecmd" ]; then
+ debug "adding rule ($rulecmd)"
+ if ! eval /sbin/devfs rule -s $rulenum $rulecmd
+ then
+ _err=1
+ break
+ fi
+ fi
+ ;;
+ esac
+ if [ $_err -ne 0 ]; then
+ debug "error in $_me"
+ break
+ fi
+ done } < $file
+ case $_opts in *f*) ;; *) set +f ;; esac
+ return $_err
+}
+
+# devfs_init_rulesets
+# Initializes rulesets from configuration files. Returns
+# non-zero if there was an error.
+#
+devfs_init_rulesets()
+{
+ local file _me
+ _me="devfs_init_rulesets"
+
+ # Go through this only once
+ if [ -n "$devfs_rulesets_init" ]; then
+ debug "$_me: devfs rulesets already initialized"
+ return
+ fi
+ for file in $devfs_rulesets; do
+ if ! devfs_rulesets_from_file $file; then
+ warn "$_me: could not read rules from $file"
+ return 1
+ fi
+ done
+ devfs_rulesets_init=1
+ debug "$_me: devfs rulesets initialized"
+ return 0
+}
+
+# devfs_set_ruleset ruleset [dir]
+# Sets the default ruleset of dir to ruleset. The ruleset argument
+# must be a ruleset name as specified in devfs.rules(5) file.
+# Returns non-zero if it could not set it successfully.
+#
+devfs_set_ruleset()
+{
+ local devdir rs _me
+ [ -n "$1" ] && eval rs=\$$1 || rs=
+ [ -n "$2" ] && devdir="-m "$2"" || devdir=
+ _me="devfs_set_ruleset"
+
+ if [ -z "$rs" ]; then
+ warn "$_me: you must specify a ruleset number"
+ return 1
+ fi
+ debug "$_me: setting ruleset ($rs) on mount-point (${devdir#-m })"
+ if ! /sbin/devfs $devdir ruleset $rs; then
+ warn "$_me: unable to set ruleset $rs to ${devdir#-m }"
+ return 1
+ fi
+ return 0
+}
+
+# devfs_apply_ruleset ruleset [dir]
+# Apply ruleset number $ruleset to the devfs mountpoint $dir.
+# The ruleset argument must be a ruleset name as specified
+# in a devfs.rules(5) file. Returns 0 on success or non-zero
+# if it could not apply the ruleset.
+#
+devfs_apply_ruleset()
+{
+ local devdir rs _me
+ [ -n "$1" ] && eval rs=\$$1 || rs=
+ [ -n "$2" ] && devdir="-m "$2"" || devdir=
+ _me="devfs_apply_ruleset"
+
+ if [ -z "$rs" ]; then
+ warn "$_me: you must specify a ruleset"
+ return 1
+ fi
+ debug "$_me: applying ruleset ($rs) to mount-point (${devdir#-m })"
+ if ! /sbin/devfs $devdir rule -s $rs applyset; then
+ warn "$_me: unable to apply ruleset $rs to ${devdir#-m }"
+ return 1
+ fi
+ return 0
+}
+
+# devfs_domount dir [ruleset]
+# Mount devfs on dir. If ruleset is specified it is set
+# on the mount-point. It must also be a ruleset name as specified
+# in a devfs.rules(5) file. Returns 0 on success.
+#
+devfs_domount()
+{
+ local devdir rs _me
+ devdir="$1"
+ [ -n "$2" ] && rs=$2 || rs=
+ _me="devfs_domount()"
+
+ if [ -z "$devdir" ]; then
+ warn "$_me: you must specify a mount-point"
+ return 1
+ fi
+ debug "$_me: mount-point is ($devdir), ruleset is ($rs)"
+ if ! mount -t devfs dev "$devdir"; then
+ warn "$_me: Unable to mount devfs on $devdir"
+ return 1
+ fi
+ if [ -n "$rs" ]; then
+ devfs_init_rulesets
+ devfs_set_ruleset $rs $devdir
+ devfs -m $devdir rule applyset
+ fi
+ return 0
+}
+
+# Provide a function for normalizing the mounting of memory
+# filesystems. This should allow the rest of the code here to remain
+# as close as possible between 5-current and 4-stable.
+# $1 = size
+# $2 = mount point
+# $3 = (optional) extra mdmfs flags
+mount_md()
+{
+ if [ -n "$3" ]; then
+ flags="$3"
+ fi
+ /sbin/mdmfs $flags -s $1 ${mfs_type} $2
+}
+
+# Code common to scripts that need to load a kernel module
+# if it isn't in the kernel yet. Syntax:
+# load_kld [-e regex] [-m module] file
+# where -e or -m chooses the way to check if the module
+# is already loaded:
+# regex is egrep'd in the output from `kldstat -v',
+# module is passed to `kldstat -m'.
+# The default way is as though `-m file' were specified.
+load_kld()
+{
+ local _loaded _mod _opt _re
+
+ while getopts "e:m:" _opt; do
+ case "$_opt" in
+ e) _re="$OPTARG" ;;
+ m) _mod="$OPTARG" ;;
+ *) err 3 'USAGE: load_kld [-e regex] [-m module] file' ;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ if [ $# -ne 1 ]; then
+ err 3 'USAGE: load_kld [-e regex] [-m module] file'
+ fi
+ _mod=${_mod:-$1}
+ _loaded=false
+ if [ -n "$_re" ]; then
+ if kldstat -v | egrep -q -e "$_re"; then
+ _loaded=true
+ fi
+ else
+ if kldstat -q -m "$_mod"; then
+ _loaded=true
+ fi
+ fi
+ if ! $_loaded; then
+ if ! kldload "$1"; then
+ warn "Unable to load kernel module $1"
+ return 1
+ else
+ info "$1 kernel module loaded."
+ if [ -f "/etc/sysctl.kld.d/$1.conf" ]; then
+ sysctl -f "/etc/sysctl.kld.d/$1.conf"
+ fi
+ fi
+ else
+ debug "load_kld: $1 kernel module already loaded."
+ fi
+ return 0
+}
+
+# ltr str src dst [var]
+# Change every $src in $str to $dst.
+# Useful when /usr is not yet mounted and we cannot use tr(1), sed(1) nor
+# awk(1). If var is non-NULL, set it to the result.
+ltr()
+{
+ local _str _src _dst _out _com _var
+ _str="$1"
+ _src="$2"
+ _dst="$3"
+ _var="$4"
+ _out=""
+
+ local IFS="${_src}"
+ for _com in ${_str}; do
+ if [ -z "${_out}" ]; then
+ _out="${_com}"
+ else
+ _out="${_out}${_dst}${_com}"
+ fi
+ done
+ if [ -n "${_var}" ]; then
+ setvar "${_var}" "${_out}"
+ else
+ echo "${_out}"
+ fi
+}
+
+# Creates a list of providers for GELI encryption.
+geli_make_list()
+{
+ local devices devices2
+ local provider mountpoint type options rest
+
+ # Create list of GELI providers from fstab.
+ while read provider mountpoint type options rest ; do
+ case ":${options}" in
+ :*noauto*)
+ noauto=yes
+ ;;
+ *)
+ noauto=no
+ ;;
+ esac
+
+ case ":${provider}" in
+ :#*)
+ continue
+ ;;
+ *.eli)
+ # Skip swap devices.
+ if [ "${type}" = "swap" -o "${options}" = "sw" -o "${noauto}" = "yes" ]; then
+ continue
+ fi
+ devices="${devices} ${provider}"
+ ;;
+ esac
+ done < /etc/fstab
+
+ # Append providers from geli_devices.
+ devices="${devices} ${geli_devices}"
+
+ for provider in ${devices}; do
+ provider=${provider%.eli}
+ provider=${provider#/dev/}
+ devices2="${devices2} ${provider}"
+ done
+
+ echo ${devices2}
+}
+
+# Originally, root mount hold had to be released before mounting
+# the root filesystem. This delayed the boot, so it was changed
+# to only wait if the root device isn't readily available. This
+# can result in rc scripts executing before all the devices - such
+# as graid(8), or USB disks - can be accessed. This function can
+# be used to explicitly wait for root mount holds to be released.
+root_hold_wait()
+{
+ local wait waited holders
+
+ waited=0
+ while true; do
+ holders="$(sysctl -n vfs.root_mount_hold)"
+ if [ -z "${holders}" ]; then
+ break;
+ fi
+ if [ ${waited} -eq 0 ]; then
+ echo -n "Waiting ${root_hold_delay}s" \
+ "for the root mount holders: ${holders}"
+ else
+ echo -n .
+ fi
+ if [ ${waited} -ge ${root_hold_delay} ]; then
+ echo
+ break
+ fi
+ sleep 1
+ waited=$(($waited + 1))
+ done
+}
+
+# Find scripts in local_startup directories that use the old syntax
+#
+find_local_scripts_old() {
+ zlist=''
+ slist=''
+ for dir in ${local_startup}; do
+ if [ -d "${dir}" ]; then
+ for file in ${dir}/[0-9]*.sh; do
+ grep '^# PROVIDE:' $file >/dev/null 2>&1 &&
+ continue
+ zlist="$zlist $file"
+ done
+ for file in ${dir}/[!0-9]*.sh; do
+ grep '^# PROVIDE:' $file >/dev/null 2>&1 &&
+ continue
+ slist="$slist $file"
+ done
+ fi
+ done
+}
+
+find_local_scripts_new() {
+ local_rc=''
+ for dir in ${local_startup}; do
+ if [ -d "${dir}" ]; then
+ for file in `grep -l '^# PROVIDE:' ${dir}/* 2>/dev/null`; do
+ case "$file" in
+ *.sample|*.pkgsave) ;;
+ *) if [ -x "$file" ]; then
+ local_rc="${local_rc} ${file}"
+ fi
+ ;;
+ esac
+ done
+ fi
+ done
+}
+
+find_system_scripts() {
+ system_rc=''
+ for file in /etc/rc.d/*; do
+ case "${file##*/}" in
+ *.pkgsave) ;;
+ *) if [ -x "$file" ]; then
+ system_rc="${system_rc} ${file}"
+ fi
+ ;;
+ esac
+ done
+}
+
+# check_required_{before|after} command
+# Check for things required by the command before and after its precmd,
+# respectively. The two separate functions are needed because some
+# conditions should prevent precmd from being run while other things
+# depend on precmd having already been run.
+#
+check_required_before()
+{
+ local _f
+
+ case "$1" in
+ start)
+ for _f in $required_vars; do
+ if ! checkyesno $_f; then
+ warn "\$${_f} is not enabled."
+ if [ -z "$rc_force" ]; then
+ return 1
+ fi
+ fi
+ done
+
+ for _f in $required_dirs; do
+ if [ ! -d "${_f}/." ]; then
+ warn "${_f} is not a directory."
+ if [ -z "$rc_force" ]; then
+ return 1
+ fi
+ fi
+ done
+
+ for _f in $required_files; do
+ if [ ! -r "${_f}" ]; then
+ warn "${_f} is not readable."
+ if [ -z "$rc_force" ]; then
+ return 1
+ fi
+ fi
+ done
+ ;;
+ esac
+
+ return 0
+}
+
+check_required_after()
+{
+ local _f _args
+
+ case "$1" in
+ start)
+ for _f in $required_modules; do
+ case "${_f}" in
+ *~*) _args="-e ${_f#*~} ${_f%%~*}" ;;
+ *:*) _args="-m ${_f#*:} ${_f%%:*}" ;;
+ *) _args="${_f}" ;;
+ esac
+ if ! load_kld ${_args}; then
+ if [ -z "$rc_force" ]; then
+ return 1
+ fi
+ fi
+ done
+ ;;
+ esac
+
+ return 0
+}
+
+# check_jail mib
+# Return true if security.jail.$mib exists and is set to 1.
+
+check_jail()
+{
+ local _mib _v
+
+ _mib=$1
+ if _v=$(${SYSCTL_N} "security.jail.$_mib" 2> /dev/null); then
+ case $_v in
+ 1) return 0;;
+ esac
+ fi
+ return 1
+}
+
+# check_kern_features mib
+# Return existence of kern.features.* sysctl MIB as true or
+# false. The result will be cached in $_rc_cache_kern_features_
+# namespace. "0" means the kern.features.X exists.
+
+check_kern_features()
+{
+ local _v
+
+ [ -n "$1" ] || return 1;
+ eval _v=\$_rc_cache_kern_features_$1
+ [ -n "$_v" ] && return "$_v";
+
+ if ${SYSCTL_N} kern.features.$1 > /dev/null 2>&1; then
+ eval _rc_cache_kern_features_$1=0
+ return 0
+ else
+ eval _rc_cache_kern_features_$1=1
+ return 1
+ fi
+}
+
+# check_namevarlist var
+# Return "0" if ${name}_var is reserved in rc.subr.
+
+_rc_namevarlist="program chroot chdir env flags fib nice user group groups prepend setup"
+check_namevarlist()
+{
+ local _v
+
+ for _v in $_rc_namevarlist; do
+ case $1 in
+ $_v) return 0 ;;
+ esac
+ done
+
+ return 1
+}
+
+# _echoonce var msg mode
+# mode=0: Echo $msg if ${$var} is empty.
+# After doing echo, a string is set to ${$var}.
+#
+# mode=1: Echo $msg if ${$var} is a string with non-zero length.
+#
+_echoonce()
+{
+ local _var _msg _mode
+ eval _var=\$$1
+ _msg=$2
+ _mode=$3
+
+ case $_mode in
+ 1) [ -n "$_var" ] && echo "$_msg" ;;
+ *) [ -z "$_var" ] && echo -n "$_msg" && eval "$1=finished" ;;
+ esac
+}
+
+# _find_rcvar var
+# Find the rc.conf file (other than /etc/defaults/rc.conf) that sets $var.
+_find_rcvar()
+{
+ local _var _dir _files
+
+ [ -n "$1" ] || return 1
+ _var="$1"; shift
+
+ _files="/etc/rc.conf"
+ for _dir in /etc ${local_startup}; do
+ for _name in $_loaded_services; do
+ _files="${_dir%/rc.d}/rc.conf.d/${_name} ${_files}"
+ done
+ done
+
+ /usr/bin/grep 2>/dev/null -rl "^${_var}=" $_files | /usr/bin/head -1
+}
+
+# write_rcvar var value
+# Add or replace the rc var $var with the value $value.
+# Look for a current setting of $var in /etc/rc.conf or /etc/rc.conf.d/$name,
+# and if found, modify it there; otherwise, append to /etc/rc.conf.
+write_rcvar()
+{
+ local _var _value _file _dir
+
+ [ -n "$1" ] || return 1
+ _var="$1"; shift
+ [ -n "$1" ] || return 1
+ _value="$1"; shift
+
+ _file="$(_find_rcvar "$_var")"
+ if [ -n "$_file" ]; then
+ local _=$'\01'
+ /usr/bin/sed -i '' "s${_}^${_var}=.*${_}${_var}=\"$_value\"${_}" "$_file"
+ echo $_file
+ return
+ fi
+
+ for _dir in /etc ${local_startup}; do
+ _file="${_dir%/rc.d}/rc.conf.d/${name}"
+ if [ -f "$_file" ]; then
+ echo "${_var}=\"${_value}\"" >>"$_file"
+ echo "$_file"
+ return
+ fi
+ done
+
+ echo "${_var}=\"${_value}\"" >>/etc/rc.conf
+ echo "/etc/rc.conf"
+}
+
+# delete_rcvar var
+# Remove the rc var $var.
+# Look for a current setting of $var in /etc/rc.conf or /etc/rc.conf.d/$name,
+# and if found, remove it. If service_delete_empty is enabled, and the
+# resulting file is empty, also delete the file.
+delete_rcvar()
+{
+ local _var _files
+
+ [ -n "$1" ] || return 1
+ _var="$1"; shift
+
+ _file="$(_find_rcvar "$_var")"
+ if [ -n "$_file" ]; then
+ /usr/bin/sed -i '' "/^${_var}=/d" "$_file"
+ echo "$_var deleted in $_file"
+
+ if checkyesno service_delete_empty && [ ! -s "$_file" ]; then
+ /bin/rm -f "$_file"
+ echo "Empty file $_file removed"
+ fi
+ fi
+}
+
+# If the loader env variable rc.debug is set, turn on debugging. rc.conf will
+# still override this, but /etc/defaults/rc.conf can't unconditionally set this
+# since it would undo what we've done here.
+if kenv -q rc.debug > /dev/null ; then
+ rc_debug=YES
+fi
+
+boottrace_cmd=`command -v boottrace`
+if [ -n "$boottrace_cmd" ] && [ "`${SYSCTL_N} -q kern.boottrace.enabled`" = "1" ]; then
+ rc_boottrace=YES
+fi
+
+SED=${SED:-$(Exists -x /usr/bin/sed /rescue/sed)}
+
+# Allow for local additions and overrides.
+# Use vdot to ensure the file has not been tampered with.
+vdot /etc/local.rc.subr
+
+# Avoid noise - when we do not have /usr mounted,
+# and we cannot use safe_dot without sed.
+if ! have basename; then
+ basename()
+ {
+ local b=${1%$2}
+ echo ${b##*/}
+ }
+ tty()
+ {
+ return 0
+ }
+ # we cannot use safe_dot without sed
+ [ -z "$SED" ] && _SAFE_EVAL_SH=:
+fi
+# safe_eval.sh provides safe_dot - for untrusted files
+$_SAFE_EVAL_SH vdot /libexec/safe_eval.sh
+$_DEBUG_SH vdot /libexec/debug.sh
+
+# Ensure we can still operate if debug.sh and
+# safe_eval.sh are not found.
+if ! have DebugOn; then
+ DebugOn() { return 0; }
+ DebugOff() {
+ local _rc=0
+ while :
+ do
+ case "$1" in
+ -[eo]) shift;; # ignore it
+ rc=*) eval "_$1"; shift;;
+ *) break;;
+ esac
+ done
+ return $_rc
+ }
+fi
+if ! have safe_dot; then
+ safe_dot() { dot "$@"; }
+fi
diff --git a/libexec/rc/rc.suspend b/libexec/rc/rc.suspend
new file mode 100755
index 000000000000..8990be81466c
--- /dev/null
+++ b/libexec/rc/rc.suspend
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+# Copyright (c) 1999 Mitsuru IWASAKI
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# sample run command file for APM Suspend Event
+
+if [ $# -ne 2 ]; then
+ echo "Usage: $0 [apm|acpi] [standby,suspend|1-4]"
+ exit 1
+fi
+
+subsystem=$1
+state=$2
+
+if [ -r /var/run/rc.suspend.pid ]; then
+ exit 1
+fi
+
+echo $$ 2> /dev/null > /var/run/rc.suspend.pid
+
+# If a device driver has problems suspending, try unloading it before
+# suspend and reloading it on resume. Example:
+# kldunload usb
+
+. /etc/rc.subr
+
+load_rc_config
+
+rcorder_opts="-k suspend"
+
+case ${local_startup} in
+[Nn][Oo] | '') ;;
+*) find_local_scripts_new ;;
+esac
+
+files=`rcorder ${rcorder_opts} /etc/rc.d/* ${local_rc} 2>/dev/null`
+
+for _rc_elem in $files; do
+ debug "run_rc_script $_rc_elem suspend"
+ run_rc_script $_rc_elem suspend
+done
+
+/usr/bin/logger -t $subsystem suspend at `/bin/date +'%Y%m%d %H:%M:%S'`
+/bin/sync && /bin/sync && /bin/sync
+/bin/sleep 3
+
+/bin/rm -f /var/run/rc.suspend.pid
+if [ $subsystem = "apm" ]; then
+ /usr/sbin/zzz
+else
+ # Notify the kernel to continue the suspend process
+ /usr/sbin/acpiconf -k 0
+fi
+
+exit 0
diff --git a/libexec/rc/safe_eval.sh b/libexec/rc/safe_eval.sh
new file mode 100644
index 000000000000..6c23b4c98218
--- /dev/null
+++ b/libexec/rc/safe_eval.sh
@@ -0,0 +1,100 @@
+:
+# RCSid:
+# $Id: safe_eval.sh,v 1.25 2025/08/07 22:13:03 sjg Exp $
+#
+# @(#) Copyright (c) 2023-2024 Simon J. Gerraty
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+
+_SAFE_EVAL_SH=:
+
+# does local *actually* work?
+local_works() {
+ local _fu
+}
+
+if local_works > /dev/null 2>&1; then
+ _local=local
+else
+ _local=:
+fi
+
+##
+# safe_set
+#
+# return a safe variable setting
+# any non-alphanumeric chars are replaced with '_'
+#
+safe_set() {
+ ${SED:-sed} 's/[ ]*#.*//;/^[A-Za-z_][A-Za-z0-9_]*=/!d;s;[^A-Za-z0-9_. "$,/=:+-];_;g'
+}
+
+##
+# safe_eval [file]
+#
+# eval variable assignments only from file
+# taking care to eliminate any shell meta chars
+#
+safe_eval() {
+ eval `cat "$@" | safe_set`
+}
+
+##
+# safe_eval_export [file]
+#
+# eval variable assignments only from file
+# taking care to eliminate any shell meta chars
+# export any variables thus set
+#
+safe_eval_export() {
+ eval `cat "$@" | safe_set | ${SED:-sed} 's/^\([^=]*\)=.*/&; export \1/'`
+}
+
+##
+# safe_dot file [...]
+#
+# feed all "file" that exist to safe_eval
+#
+safe_dot() {
+ eval $_local ef ex f rc
+ ef=
+ ex=
+ rc=1
+ while :
+ do
+ case "$1" in
+ --export) ex=_export; shift;;
+ *) break;;
+ esac
+ done
+ for f in "$@"
+ do
+ test -s "$f" -a -f "$f" || continue
+ : check for space or tab in "$f"
+ case "$f" in
+ *[[:space:]]*|*" "*|*" "*) # we cannot do this efficiently
+ dotted="$dotted $f"
+ safe_eval$ex "$f"
+ rc=$?
+ continue
+ ;;
+ esac
+ ef="${ef:+$ef }$f"
+ dotted="$dotted $f"
+ done
+ test -z "$ef" && return $rc
+ safe_eval$ex $ef
+ return 0
+}
+
+case /$0 in
+*/safe_eval*)
+ case "$1" in
+ dot|eval|set) op=safe_$1; shift; $op "$@";;
+ *) safe_dot "$@";;
+ esac
+ ;;
+esac
diff --git a/libexec/rc/tests/Makefile b/libexec/rc/tests/Makefile
new file mode 100644
index 000000000000..c44c6db90b77
--- /dev/null
+++ b/libexec/rc/tests/Makefile
@@ -0,0 +1,3 @@
+ATF_TESTS_SH+= rc_subr_test
+
+.include <bsd.test.mk>
diff --git a/libexec/rc/tests/rc_subr_test.sh b/libexec/rc/tests/rc_subr_test.sh
new file mode 100644
index 000000000000..9ddd13b61a7c
--- /dev/null
+++ b/libexec/rc/tests/rc_subr_test.sh
@@ -0,0 +1,148 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright 2022 Mateusz Piotrowski <0mp@FreeBSD.org>
+# Copyright (c) 2025 Klara, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+atf_test_case no_cycles
+no_cycles_head()
+{
+ atf_set "descr" "Verify that /etc/rc.d/* contains no cycles"
+}
+
+no_cycles_body()
+{
+ atf_check -e empty -o ignore rcorder /etc/rc.d/*
+}
+
+atf_test_case oomprotect_all
+oomprotect_all_head()
+{
+ atf_set "descr" "Verify that \${name}_oomprotect=all protects " \
+ "the command and all its current and future children"
+ atf_set "require.user" "root" # For protect(1).
+}
+
+oomprotect_all_body()
+{
+ if [ "$(sysctl -n security.jail.jailed)" != 0 ]; then
+ atf_skip "protect(1) cannot be used in a jail"
+ fi
+
+ __name="$(atf_get ident)"
+ __pidfile="$(mktemp -t "${__name}.pid")"
+ __childpidfile="$(mktemp -t "${__name}.childpid")"
+ __script=$(mktemp -t "${__name}.script")
+
+ cat >> "$__script" <<-'LITERAL'
+ . /etc/rc.subr
+ name="$1"
+ pidfile="$2"
+ _childpidfile="$3"
+ _rc_arg="$4"
+ setvar "${name}_oomprotect" all
+ command="/usr/sbin/daemon"
+ command_args="-P $pidfile -p $_childpidfile -- /bin/sleep 60"
+ run_rc_command "$_rc_arg"
+ LITERAL
+
+ atf_check -s exit:0 -o inline:"Starting ${__name}.\n" -e empty \
+ /bin/sh "$__script" "$__name" "$__pidfile" "$__childpidfile" onestart
+ atf_check -s exit:0 -o match:'^..1..... .......1$' -e empty \
+ ps -p "$(cat "$__pidfile")" -o flags,flags2
+ atf_check -s exit:0 -o match:'^..1..... .......1$' -e empty \
+ ps -p "$(cat "$__childpidfile")" -o flags,flags2
+ atf_check -s exit:0 -o ignore -e empty \
+ /bin/sh "$__script" "$__name" "$__pidfile" "$__childpidfile" onestop
+}
+
+atf_test_case oomprotect_yes
+oomprotect_yes_head()
+{
+ atf_set "descr" "Verify that \${name}_oomprotect=yes protects " \
+ "the command but not its children"
+ atf_set "require.user" "root" # For protect(1).
+}
+
+oomprotect_yes_body()
+{
+ if [ "$(sysctl -n security.jail.jailed)" != 0 ]; then
+ atf_skip "protect(1) cannot be used in a jail"
+ fi
+
+ __name="$(atf_get ident)"
+ __pidfile="$(mktemp -t "${__name}.pid")"
+ __script=$(mktemp -t "${__name}.script")
+
+ cat >> "$__script" <<-'LITERAL'
+ . /etc/rc.subr
+ name="$1"
+ pidfile="$2"
+ _rc_arg="$3"
+ setvar "${name}_oomprotect" yes
+ procname="/bin/sleep"
+ command="/usr/sbin/daemon"
+ command_args="-p $pidfile -- $procname 60"
+ run_rc_command "$_rc_arg"
+ LITERAL
+
+ atf_check -s exit:0 -o inline:"Starting ${__name}.\n" -e empty \
+ /bin/sh "$__script" "$__name" "$__pidfile" onestart
+ atf_check -s exit:0 -o match:'^..1..... .......0$' -e empty \
+ ps -p "$(cat "$__pidfile")" -ax -o flags,flags2
+ atf_check -s exit:0 -o ignore -e empty \
+ /bin/sh "$__script" "$__name" "$__pidfile" onestop
+}
+
+atf_test_case wait_for_pids_progress
+wait_for_pids_progress_head()
+{
+ atf_set "descr" "Verify that wait_for_pids prints progress updates"
+}
+wait_for_pids_progress_body()
+{
+ cat >>script <<'EOF'
+. /etc/rc.subr
+sleep 15 &
+a=$!
+sleep 10 &
+b=$!
+sleep 5 &
+c=$!
+wait_for_pids $a $b $c
+EOF
+ re="^Waiting for PIDS: [0-9]+ [0-9]+ [0-9]+"
+ re="${re}, [0-9]+ [0-9]+"
+ re="${re}, [0-9]+\.$"
+ atf_check -s exit:0 -o match:"${re}" /bin/sh script
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case no_cycles
+ atf_add_test_case oomprotect_all
+ atf_add_test_case oomprotect_yes
+ atf_add_test_case wait_for_pids_progress
+}
diff --git a/libexec/revnetgroup/Makefile b/libexec/revnetgroup/Makefile
new file mode 100644
index 000000000000..d764028e5768
--- /dev/null
+++ b/libexec/revnetgroup/Makefile
@@ -0,0 +1,8 @@
+PROG= revnetgroup
+SRCS= revnetgroup.c hash.c parse_netgroup.c
+
+MAN= revnetgroup.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/libexec/revnetgroup/Makefile.depend b/libexec/revnetgroup/Makefile.depend
new file mode 100644
index 000000000000..6ef78fac5cbf
--- /dev/null
+++ b/libexec/revnetgroup/Makefile.depend
@@ -0,0 +1,15 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/revnetgroup/hash.c b/libexec/revnetgroup/hash.c
new file mode 100644
index 000000000000..db8e95e3040c
--- /dev/null
+++ b/libexec/revnetgroup/hash.c
@@ -0,0 +1,205 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "hash.h"
+
+/*
+ * This hash function is stolen directly from the
+ * Berkeley DB package. It already exists inside libc, but
+ * it's declared static which prevents us from calling it
+ * from here.
+ */
+/*
+ * OZ's original sdbm hash
+ */
+u_int32_t
+hash(const void *keyarg, size_t len)
+{
+ const u_char *key;
+ size_t loop;
+ u_int32_t h;
+
+#define HASHC h = *key++ + 65599 * h
+
+ h = 0;
+ key = keyarg;
+ if (len > 0) {
+ loop = (len + 8 - 1) >> 3;
+
+ switch (len & (8 - 1)) {
+ case 0:
+ do {
+ HASHC;
+ /* FALLTHROUGH */
+ case 7:
+ HASHC;
+ /* FALLTHROUGH */
+ case 6:
+ HASHC;
+ /* FALLTHROUGH */
+ case 5:
+ HASHC;
+ /* FALLTHROUGH */
+ case 4:
+ HASHC;
+ /* FALLTHROUGH */
+ case 3:
+ HASHC;
+ /* FALLTHROUGH */
+ case 2:
+ HASHC;
+ /* FALLTHROUGH */
+ case 1:
+ HASHC;
+ } while (--loop);
+ }
+ }
+ return (h);
+}
+
+/*
+ * Generate a hash value for a given key (character string).
+ * We mask off all but the lower 8 bits since our table array
+ * can only hold 256 elements.
+ */
+u_int32_t
+hashkey(char *key)
+{
+
+ if (key == NULL)
+ return (-1);
+ return(hash((void *)key, strlen(key)) & HASH_MASK);
+}
+
+/* Find an entry in the hash table (may be hanging off a linked list). */
+char *
+lookup(struct group_entry *table[], char *key)
+{
+ struct group_entry *cur;
+
+ cur = table[hashkey(key)];
+
+ while (cur) {
+ if (!strcmp(cur->key, key))
+ return(cur->data);
+ cur = cur->next;
+ }
+
+ return(NULL);
+}
+
+/*
+ * Store an entry in the main netgroup hash table. Here's how this
+ * works: the table can only be so big when we initialize it (TABLESIZE)
+ * but the number of netgroups in the /etc/netgroup file could easily be
+ * much larger than the table. Since our hash values are adjusted to
+ * never be greater than TABLESIZE too, this means it won't be long before
+ * we find ourselves with two keys that hash to the same value.
+ *
+ * One way to deal with this is to malloc(2) a second table and start
+ * doing indirection, but this is a pain in the butt and it's not worth
+ * going to all that trouble for a dinky little program like this. Instead,
+ * we turn each table entry into a linked list and simply link keys
+ * with the same hash value together at the same index location within
+ * the table.
+ *
+ * That's a lot of comment for such a small piece of code, isn't it.
+ */
+void
+store(struct group_entry *table[], char *key, char *data)
+{
+ struct group_entry *new;
+ u_int32_t i;
+
+ i = hashkey(key);
+
+ new = (struct group_entry *)malloc(sizeof(struct group_entry));
+ new->key = strdup(key);
+ new->data = strdup(data);
+ new->next = table[i];
+ table[i] = new;
+
+ return;
+}
+
+/*
+ * Store a group member entry and/or update its grouplist. This is
+ * a bit more complicated than the previous function since we have to
+ * maintain not only the hash table of group members, each group member
+ * structure also has a linked list of groups hung off it. If handed
+ * a member name that we haven't encountered before, we have to do
+ * two things: add that member to the table (possibly hanging them
+ * off the end of a linked list, as above), and add a group name to
+ * the member's grouplist list. If we're handed a name that already has
+ * an entry in the table, then we just have to do one thing, which is
+ * to update its grouplist.
+ */
+void
+mstore(struct member_entry *table[], char *key, char *data, char *domain)
+{
+ struct member_entry *cur, *new;
+ struct grouplist *tmp;
+ u_int32_t i;
+
+ i = hashkey(key);
+ cur = table[i];
+
+ tmp = (struct grouplist *)malloc(sizeof(struct grouplist));
+ tmp->groupname = strdup(data);
+ tmp->next = NULL;
+
+ /* Check if all we have to do is insert a new groupname. */
+ while (cur) {
+ if (!strcmp(cur->key, key)) {
+ tmp->next = cur->groups;
+ cur->groups = tmp;
+ return;
+ }
+ cur = cur->next;
+ }
+
+ /* Didn't find a match -- add the whole mess to the table. */
+ new = (struct member_entry *)malloc(sizeof(struct member_entry));
+ new->key = strdup(key);
+ new->domain = domain ? strdup(domain) : "*";
+ new->groups = tmp;
+ new->next = table[i];
+ table[i] = new;
+
+ return;
+}
diff --git a/libexec/revnetgroup/hash.h b/libexec/revnetgroup/hash.h
new file mode 100644
index 000000000000..debd9d199d1d
--- /dev/null
+++ b/libexec/revnetgroup/hash.h
@@ -0,0 +1,67 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Groupname entry hung off a member_entry node. */
+struct grouplist {
+ char *groupname;
+ struct grouplist *next;
+};
+
+/* Entry in the cooked member list hash table. */
+struct member_entry {
+ char *key;
+ char *domain;
+ struct grouplist *groups;
+ struct member_entry *next;
+};
+
+/* Entry in the raw netgroup table. */
+struct group_entry {
+ char *key;
+ char *data;
+ struct group_entry *next;
+};
+
+/* Table size (chosen arbitrarily). Not too big, not too small. */
+#define TABLESIZE 256
+#define HASH_MASK 0x000000FF
+
+#define LINSIZ 1024 * 10
+
+extern void store(struct group_entry ** , char *, char *);
+extern void mstore(struct member_entry ** , char *, char *, char *);
+extern char *lookup(struct group_entry **, char *);
+extern void __endnetgrent(void);
+extern void __setnetgrent(char *);
+extern int __getnetgrent(char **, char **, char **);
diff --git a/libexec/revnetgroup/parse_netgroup.c b/libexec/revnetgroup/parse_netgroup.c
new file mode 100644
index 000000000000..3d6a7939fa1d
--- /dev/null
+++ b/libexec/revnetgroup/parse_netgroup.c
@@ -0,0 +1,357 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This is a specially hacked-up version of getnetgrent.c used to parse
+ * data from the stored hash table of netgroup info rather than from a
+ * file. It's used mainly for the parse_netgroup() function. All the YP
+ * stuff and file support has been stripped out since it isn't needed.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "hash.h"
+
+/*
+ * Static Variables and functions used by setnetgrent(), getnetgrent() and
+ * __endnetgrent().
+ * There are two linked lists:
+ * - linelist is just used by setnetgrent() to parse the net group file via.
+ * parse_netgrp()
+ * - netgrp is the list of entries for the current netgroup
+ */
+struct linelist {
+ struct linelist *l_next; /* Chain ptr. */
+ int l_parsed; /* Flag for cycles */
+ char *l_groupname; /* Name of netgroup */
+ char *l_line; /* Netgroup entrie(s) to be parsed */
+};
+
+struct netgrp {
+ struct netgrp *ng_next; /* Chain ptr */
+ char *ng_str[3]; /* Field pointers, see below */
+};
+#define NG_HOST 0 /* Host name */
+#define NG_USER 1 /* User name */
+#define NG_DOM 2 /* and Domain name */
+
+static struct linelist *linehead = (struct linelist *)0;
+static struct netgrp *nextgrp = (struct netgrp *)0;
+static struct {
+ struct netgrp *gr;
+ char *grname;
+} grouphead = {
+ (struct netgrp *)0,
+ (char *)0,
+};
+static int parse_netgrp(char *group);
+static struct linelist *read_for_group(char *group);
+extern struct group_entry *gtable[];
+
+/*
+ * setnetgrent()
+ * Parse the netgroup file looking for the netgroup and build the list
+ * of netgrp structures. Let parse_netgrp() and read_for_group() do
+ * most of the work.
+ */
+void
+__setnetgrent(char *group)
+{
+ /* Sanity check */
+
+ if (group == NULL || !strlen(group))
+ return;
+
+ if (grouphead.gr == (struct netgrp *)0 ||
+ strcmp(group, grouphead.grname)) {
+ __endnetgrent();
+ if (parse_netgrp(group))
+ __endnetgrent();
+ else {
+ grouphead.grname = (char *)
+ malloc(strlen(group) + 1);
+ strcpy(grouphead.grname, group);
+ }
+ }
+ nextgrp = grouphead.gr;
+}
+
+/*
+ * Get the next netgroup off the list.
+ */
+int
+__getnetgrent(char **hostp, char **userp, char **domp)
+{
+ if (nextgrp) {
+ *hostp = nextgrp->ng_str[NG_HOST];
+ *userp = nextgrp->ng_str[NG_USER];
+ *domp = nextgrp->ng_str[NG_DOM];
+ nextgrp = nextgrp->ng_next;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * __endnetgrent() - cleanup
+ */
+void
+__endnetgrent(void)
+{
+ struct linelist *lp, *olp;
+ struct netgrp *gp, *ogp;
+
+ lp = linehead;
+ while (lp) {
+ olp = lp;
+ lp = lp->l_next;
+ free(olp->l_groupname);
+ free(olp->l_line);
+ free((char *)olp);
+ }
+ linehead = (struct linelist *)0;
+ if (grouphead.grname) {
+ free(grouphead.grname);
+ grouphead.grname = (char *)0;
+ }
+ gp = grouphead.gr;
+ while (gp) {
+ ogp = gp;
+ gp = gp->ng_next;
+ if (ogp->ng_str[NG_HOST])
+ free(ogp->ng_str[NG_HOST]);
+ if (ogp->ng_str[NG_USER])
+ free(ogp->ng_str[NG_USER]);
+ if (ogp->ng_str[NG_DOM])
+ free(ogp->ng_str[NG_DOM]);
+ free((char *)ogp);
+ }
+ grouphead.gr = (struct netgrp *)0;
+}
+
+/*
+ * Parse the netgroup file setting up the linked lists.
+ */
+static int
+parse_netgrp(char *group)
+{
+ char *spos, *epos;
+ int len, strpos;
+#ifdef DEBUG
+ int fields;
+#endif
+ char *pos, *gpos;
+ struct netgrp *grp;
+ struct linelist *lp = linehead;
+
+ /*
+ * First, see if the line has already been read in.
+ */
+ while (lp) {
+ if (!strcmp(group, lp->l_groupname))
+ break;
+ lp = lp->l_next;
+ }
+ if (lp == (struct linelist *)0 &&
+ (lp = read_for_group(group)) == (struct linelist *)0)
+ return (1);
+ if (lp->l_parsed) {
+#ifdef DEBUG
+ /*
+ * This error message is largely superfluous since the
+ * code handles the error condition successfully, and
+ * spewing it out from inside libc can actually hose
+ * certain programs.
+ */
+ warnx("cycle in netgroup %s", lp->l_groupname);
+#endif
+ return (1);
+ } else
+ lp->l_parsed = 1;
+ pos = lp->l_line;
+ /* Watch for null pointer dereferences, dammit! */
+ while (pos != NULL && *pos != '\0') {
+ if (*pos == '(') {
+ grp = (struct netgrp *)malloc(sizeof (struct netgrp));
+ bzero((char *)grp, sizeof (struct netgrp));
+ grp->ng_next = grouphead.gr;
+ grouphead.gr = grp;
+ pos++;
+ gpos = strsep(&pos, ")");
+#ifdef DEBUG
+ fields = 0;
+#endif
+ for (strpos = 0; strpos < 3; strpos++) {
+ if ((spos = strsep(&gpos, ","))) {
+#ifdef DEBUG
+ fields++;
+#endif
+ while (*spos == ' ' || *spos == '\t')
+ spos++;
+ if ((epos = strpbrk(spos, " \t"))) {
+ *epos = '\0';
+ len = epos - spos;
+ } else
+ len = strlen(spos);
+ if (len > 0) {
+ grp->ng_str[strpos] = (char *)
+ malloc(len + 1);
+ bcopy(spos, grp->ng_str[strpos],
+ len + 1);
+ }
+ } else {
+ /*
+ * All other systems I've tested
+ * return NULL for empty netgroup
+ * fields. It's up to user programs
+ * to handle the NULLs appropriately.
+ */
+ grp->ng_str[strpos] = NULL;
+ }
+ }
+#ifdef DEBUG
+ /*
+ * Note: on other platforms, malformed netgroup
+ * entries are not normally flagged. While we
+ * can catch bad entries and report them, we should
+ * stay silent by default for compatibility's sake.
+ */
+ if (fields < 3)
+ warnx("bad entry (%s%s%s%s%s) in netgroup \"%s\"",
+ grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST],
+ grp->ng_str[NG_USER] == NULL ? "" : ",",
+ grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER],
+ grp->ng_str[NG_DOM] == NULL ? "" : ",",
+ grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM],
+ lp->l_groupname);
+#endif
+ } else {
+ spos = strsep(&pos, ", \t");
+ if (parse_netgrp(spos))
+ continue;
+ }
+ /* Watch for null pointer dereferences, dammit! */
+ if (pos != NULL)
+ while (*pos == ' ' || *pos == ',' || *pos == '\t')
+ pos++;
+ }
+ return (0);
+}
+
+/*
+ * Read the netgroup file and save lines until the line for the netgroup
+ * is found. Return 1 if eof is encountered.
+ */
+static struct linelist *
+read_for_group(char *group)
+{
+ char *pos, *spos, *linep = NULL, *olinep = NULL;
+ int len, olen;
+ int cont;
+ struct linelist *lp;
+ char line[LINSIZ + 1];
+ char *data = NULL;
+
+ data = lookup (gtable, group);
+ sprintf(line, "%s %s", group, data);
+ pos = (char *)&line;
+#ifdef CANT_HAPPEN
+ if (*pos == '#')
+ continue;
+#endif
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+ spos = pos;
+ while (*pos != ' ' && *pos != '\t' && *pos != '\n' &&
+ *pos != '\0')
+ pos++;
+ len = pos - spos;
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+ if (*pos != '\n' && *pos != '\0') {
+ lp = (struct linelist *)malloc(sizeof (*lp));
+ lp->l_parsed = 0;
+ lp->l_groupname = (char *)malloc(len + 1);
+ bcopy(spos, lp->l_groupname, len);
+ *(lp->l_groupname + len) = '\0';
+ len = strlen(pos);
+ olen = 0;
+ /*
+ * Loop around handling line continuations.
+ */
+ do {
+ if (*(pos + len - 1) == '\n')
+ len--;
+ if (*(pos + len - 1) == '\\') {
+ len--;
+ cont = 1;
+ } else
+ cont = 0;
+ if (len > 0) {
+ linep = (char *)malloc(olen + len + 1);
+ if (olen > 0) {
+ bcopy(olinep, linep, olen);
+ free(olinep);
+ }
+ bcopy(pos, linep + olen, len);
+ olen += len;
+ *(linep + olen) = '\0';
+ olinep = linep;
+ }
+#ifdef CANT_HAPPEN
+ if (cont) {
+ if (fgets(line, LINSIZ, netf)) {
+ pos = line;
+ len = strlen(pos);
+ } else
+ cont = 0;
+ }
+#endif
+ } while (cont);
+ lp->l_line = linep;
+ lp->l_next = linehead;
+ linehead = lp;
+#ifdef CANT_HAPPEN
+ /*
+ * If this is the one we wanted, we are done.
+ */
+ if (!strcmp(lp->l_groupname, group))
+#endif
+ return (lp);
+ }
+ return ((struct linelist *)0);
+}
diff --git a/libexec/revnetgroup/revnetgroup.8 b/libexec/revnetgroup/revnetgroup.8
new file mode 100644
index 000000000000..ea22486e16da
--- /dev/null
+++ b/libexec/revnetgroup/revnetgroup.8
@@ -0,0 +1,157 @@
+.\" Copyright (c) 1995
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd October 24, 1995
+.Dt REVNETGROUP 8
+.Os
+.Sh NAME
+.Nm revnetgroup
+.Nd "generate reverse netgroup data"
+.Sh SYNOPSIS
+.Nm
+.Fl u | h
+.Op Fl f Ar netgroup_file
+.Sh DESCRIPTION
+The
+.Nm
+utility processes the contents of a file in
+.Xr netgroup 5
+format into what is called
+.Pa reverse netgroup
+form.
+That is, where the original file shows
+netgroup memberships in terms of which members reside in a particular
+group, the reverse netgroup format specifies what groups are associated
+with a particular member.
+This information is used to generate the
+.Pa netgroup.byuser
+and
+.Pa netgroup.byhost
+.Tn NIS
+maps.
+These reverse netgroup maps are used to help speed up
+netgroup lookups, particularly for the
+.Fn innetgr
+library function.
+.Pp
+For example, the standard
+.Pa /etc/netgroup
+file may list a netgroup and a list of its members.
+Here, the
+netgroup is considered the
+.Em key
+and the member names are the
+.Em data .
+By contrast, the reverse
+.Pa netgroup.byuser
+database lists each unique
+member as the key and the netgroups to which the members belong become
+the data.
+Separate databases are created to hold information pertaining
+to users and hosts; this allows netgroup username lookups
+and netgroup hostname lookups to be performed using independent keyspaces.
+.Pp
+By constructing these reverse netgroup databases (and the corresponding
+.Tn NIS
+maps) in advance, the
+.Xr getnetgrent 3
+library functions are spared from having to work out the dependencies
+themselves on the fly.
+This is important on networks with large numbers
+of users and hosts, since it can take a considerable amount of time
+to process very large netgroup databases.
+.Pp
+The
+.Nm
+utility prints its results on the standard output.
+It is usually called
+only by
+.Pa /var/yp/Makefile
+when rebuilding the
+.Tn NIS
+netgroup maps.
+.Sh OPTIONS
+The
+.Nm
+utility supports the following options:
+.Bl -tag -width indent
+.It Fl u
+Generate
+.Pa netgroup.byuser
+output; only username information in the
+original netgroup file is processed.
+.It Fl h
+Generate
+.Pa netgroup.byhost
+output; only hostname information in the
+original netgroup file is processed.
+(Note at least one of the
+.Fl u
+or
+.Fl h
+flags must be specified.)
+.It Op Fl f Ar netgroup_file
+The
+.Nm
+utility uses
+.Pa /etc/netgroup
+as its default input file.
+The
+.Fl f
+flag allows the user to specify an alternate input file.
+Specifying ``-''
+as the input file causes
+.Nm
+to read from the standard input.
+.El
+.Sh FILES
+.Bl -tag -width /var/yp/Makefile -compact
+.It Pa /var/yp/Makefile
+the Makefile that calls
+.Nm yp_mkdb
+and
+.Nm
+to build the
+.Tn NIS
+databases
+.It Pa /etc/netgroup
+the default netgroup database file.
+This file is most often found
+only on the
+.Tn NIS
+master server
+.El
+.Sh SEE ALSO
+.Xr getnetgrent 3 ,
+.Xr netgroup 5 ,
+.Xr yp 8 ,
+.Xr yp_mkdb 8
+.Sh AUTHORS
+.An Bill Paul Aq Mt wpaul@ctr.columbia.edu
diff --git a/libexec/revnetgroup/revnetgroup.c b/libexec/revnetgroup/revnetgroup.c
new file mode 100644
index 000000000000..34ec0d9491c4
--- /dev/null
+++ b/libexec/revnetgroup/revnetgroup.c
@@ -0,0 +1,176 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * reverse netgroup map generator program
+ *
+ * Written by Bill Paul <wpaul@ctr.columbia.edu>
+ * Center for Telecommunications Research
+ * Columbia University, New York City
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "hash.h"
+
+/* Default location of netgroup file. */
+char *netgroup = "/etc/netgroup";
+
+/* Stored hash table version of 'forward' netgroup database. */
+struct group_entry *gtable[TABLESIZE];
+
+/*
+ * Stored hash table of 'reverse' netgroup member database
+ * which we will construct.
+ */
+struct member_entry *mtable[TABLESIZE];
+
+static void
+usage(void)
+{
+ fprintf (stderr,"usage: revnetgroup -u | -h [-f netgroup_file]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ char readbuf[LINSIZ];
+ struct group_entry *gcur;
+ struct member_entry *mcur;
+ char *host, *user, *domain;
+ int ch;
+ char *key = NULL, *data = NULL;
+ int hosts = -1, i;
+
+ if (argc < 2)
+ usage();
+
+ while ((ch = getopt(argc, argv, "uhf:")) != -1) {
+ switch(ch) {
+ case 'u':
+ if (hosts != -1) {
+ warnx("please use only one of -u or -h");
+ usage();
+ }
+ hosts = 0;
+ break;
+ case 'h':
+ if (hosts != -1) {
+ warnx("please use only one of -u or -h");
+ usage();
+ }
+ hosts = 1;
+ break;
+ case 'f':
+ netgroup = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (hosts == -1)
+ usage();
+
+ if (strcmp(netgroup, "-")) {
+ if ((fp = fopen(netgroup, "r")) == NULL) {
+ err(1, "%s", netgroup);
+ }
+ } else {
+ fp = stdin;
+ }
+
+ /* Stuff all the netgroup names and members into a hash table. */
+ while (fgets(readbuf, LINSIZ, fp)) {
+ if (readbuf[0] == '#')
+ continue;
+ /* handle backslash line continuations */
+ while(readbuf[strlen(readbuf) - 2] == '\\') {
+ fgets((char *)&readbuf[strlen(readbuf) - 2],
+ sizeof(readbuf) - strlen(readbuf), fp);
+ }
+ data = NULL;
+ if ((data = (char *)(strpbrk(readbuf, " \t") + 1)) < (char *)2)
+ continue;
+ key = (char *)&readbuf;
+ *(data - 1) = '\0';
+ store(gtable, key, data);
+ }
+
+ fclose(fp);
+
+ /*
+ * Find all members of each netgroup and keep track of which
+ * group they belong to.
+ */
+ for (i = 0; i < TABLESIZE; i++) {
+ gcur = gtable[i];
+ while(gcur) {
+ __setnetgrent(gcur->key);
+ while(__getnetgrent(&host, &user, &domain) != 0) {
+ if (hosts ? host && strcmp(host,"-") : user && strcmp(user, "-"))
+ mstore(mtable, hosts ? host : user, gcur->key, domain);
+ }
+ gcur = gcur->next;
+ }
+ }
+
+ /* Release resources used by the netgroup parser code. */
+ __endnetgrent();
+
+ /* Spew out the results. */
+ for (i = 0; i < TABLESIZE; i++) {
+ mcur = mtable[i];
+ while(mcur) {
+ struct grouplist *tmp;
+ printf ("%s.%s\t", mcur->key, mcur->domain);
+ tmp = mcur->groups;
+ while(tmp) {
+ printf ("%s", tmp->groupname);
+ tmp = tmp->next;
+ if (tmp)
+ printf(",");
+ }
+ mcur = mcur->next;
+ printf ("\n");
+ }
+ }
+
+ /* Let the OS free all our resources. */
+ exit(0);
+}
diff --git a/libexec/rpc.rquotad/Makefile b/libexec/rpc.rquotad/Makefile
new file mode 100644
index 000000000000..f232a73b8b87
--- /dev/null
+++ b/libexec/rpc.rquotad/Makefile
@@ -0,0 +1,9 @@
+PACKAGE= nfs
+
+PROG = rpc.rquotad
+SRCS = rquotad.c
+MAN = rpc.rquotad.8
+
+LIBADD= rpcsvc util
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.rquotad/Makefile.depend b/libexec/rpc.rquotad/Makefile.depend
new file mode 100644
index 000000000000..9e6ba57ca2bf
--- /dev/null
+++ b/libexec/rpc.rquotad/Makefile.depend
@@ -0,0 +1,20 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/rpc \
+ include/rpcsvc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/librpcsvc \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rpc.rquotad/rpc.rquotad.8 b/libexec/rpc.rquotad/rpc.rquotad.8
new file mode 100644
index 000000000000..27fc1fac19df
--- /dev/null
+++ b/libexec/rpc.rquotad/rpc.rquotad.8
@@ -0,0 +1,65 @@
+.\"
+.\" Copyright (c) 1994 Theo de Raadt
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Theo de Raadt.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 22, 1994
+.Dt RPC.RQUOTAD 8
+.Os
+.Sh NAME
+.Nm rpc.rquotad
+.Nd remote quota server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rquotad
+.Sh DESCRIPTION
+The
+.Nm
+utility is a
+.Xr rpc 3
+server which returns quotas for a user of a local file system
+which is NFS-mounted onto a remote machine.
+The
+.Xr quota 1
+utility uses the results to display user quotas for remote file systems.
+The
+.Nm
+utility is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn RPC
+protocol defined in
+.Pa /usr/include/rpcsvc/rquota.x .
+.Sh SEE ALSO
+.Xr quota 1
+.Sh BUGS
+.Bx 4.4
+and
+.Fx
+support group quotas but the rquota protocol does not.
diff --git a/libexec/rpc.rquotad/rquotad.c b/libexec/rpc.rquotad/rquotad.c
new file mode 100644
index 000000000000..ce55bd87da62
--- /dev/null
+++ b/libexec/rpc.rquotad/rquotad.c
@@ -0,0 +1,314 @@
+/*
+ * by Manuel Bouyer (bouyer@ensta.fr)
+ *
+ * There is no copyright, you can use it as you want.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <ufs/ufs/quota.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rquota.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <libutil.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+static void rquota_service_1(struct svc_req *request, SVCXPRT *transp);
+static void rquota_service_2(struct svc_req *request, SVCXPRT *transp);
+static void sendquota(struct svc_req *request, SVCXPRT *transp);
+static void sendquota_extended(struct svc_req *request, SVCXPRT *transp);
+static int getfsquota(int type, long id, char *path, struct dqblk *dqblk);
+
+static int from_inetd = 1;
+static int debug = 0;
+
+static void
+cleanup(int sig)
+{
+
+ (void)sig;
+ (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
+ exit(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ SVCXPRT *transp;
+ int ok;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+ int vers;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "d")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
+ from_inetd = 0;
+
+ if (!from_inetd) {
+ if (!debug)
+ daemon(0, 0);
+ (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
+ (void)signal(SIGINT, cleanup);
+ (void)signal(SIGTERM, cleanup);
+ (void)signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ /* create and register the service */
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "couldn't create udp service.");
+ exit(1);
+ }
+ vers = RQUOTAVERS;
+ ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS,
+ rquota_service_1, NULL);
+ if (ok) {
+ vers = EXT_RQUOTAVERS;
+ ok = svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS,
+ rquota_service_2, NULL);
+ }
+ } else {
+ vers = RQUOTAVERS;
+ ok = svc_create(rquota_service_1,
+ RQUOTAPROG, RQUOTAVERS, "udp");
+ if (ok) {
+ vers = EXT_RQUOTAVERS;
+ ok = svc_create(rquota_service_2,
+ RQUOTAPROG, EXT_RQUOTAVERS, "udp");
+
+ }
+ }
+ if (!ok) {
+ syslog(LOG_ERR,
+ "unable to register (RQUOTAPROG, %s, %s)",
+ vers == RQUOTAVERS ? "RQUOTAVERS" : "EXT_RQUOTAVERS",
+ from_inetd ? "(inetd)" : "udp");
+ exit(1);
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
+
+static void
+rquota_service_2(struct svc_req *request, SVCXPRT *transp)
+{
+
+ switch (request->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
+ break;
+ case RQUOTAPROC_GETQUOTA:
+ case RQUOTAPROC_GETACTIVEQUOTA:
+ sendquota_extended(request, transp);
+ break;
+ default:
+ svcerr_noproc(transp);
+ break;
+ }
+ if (from_inetd)
+ exit(0);
+}
+
+static void
+rquota_service_1(struct svc_req *request, SVCXPRT *transp)
+{
+
+ switch (request->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
+ break;
+ case RQUOTAPROC_GETQUOTA:
+ case RQUOTAPROC_GETACTIVEQUOTA:
+ sendquota(request, transp);
+ break;
+ default:
+ svcerr_noproc(transp);
+ break;
+ }
+ if (from_inetd)
+ exit(0);
+}
+
+/* read quota for the specified id, and send it */
+static void
+sendquota(struct svc_req *request, SVCXPRT *transp)
+{
+ struct getquota_args getq_args;
+ struct getquota_rslt getq_rslt;
+ struct dqblk dqblk;
+ struct timeval timev;
+ int scale;
+
+ bzero(&getq_args, sizeof(getq_args));
+ if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
+ svcerr_decode(transp);
+ return;
+ }
+ if (request->rq_cred.oa_flavor != AUTH_UNIX) {
+ /* bad auth */
+ getq_rslt.status = Q_EPERM;
+ } else if (!getfsquota(USRQUOTA, getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
+ /* failed, return noquota */
+ getq_rslt.status = Q_NOQUOTA;
+ } else {
+ gettimeofday(&timev, NULL);
+ getq_rslt.status = Q_OK;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
+ scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
+ DEV_BSIZE * scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
+ dqblk.dqb_bhardlimit / scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
+ dqblk.dqb_bsoftlimit / scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
+ dqblk.dqb_curblocks / scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
+ dqblk.dqb_ihardlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
+ dqblk.dqb_isoftlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
+ dqblk.dqb_curinodes;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
+ dqblk.dqb_btime - timev.tv_sec;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
+ dqblk.dqb_itime - timev.tv_sec;
+ }
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
+ svcerr_systemerr(transp);
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+}
+
+static void
+sendquota_extended(struct svc_req *request, SVCXPRT *transp)
+{
+ struct ext_getquota_args getq_args;
+ struct getquota_rslt getq_rslt;
+ struct dqblk dqblk;
+ struct timeval timev;
+ int scale;
+
+ bzero(&getq_args, sizeof(getq_args));
+ if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, &getq_args)) {
+ svcerr_decode(transp);
+ return;
+ }
+ if (request->rq_cred.oa_flavor != AUTH_UNIX) {
+ /* bad auth */
+ getq_rslt.status = Q_EPERM;
+ } else if (!getfsquota(getq_args.gqa_type, getq_args.gqa_id, getq_args.gqa_pathp, &dqblk)) {
+ /* failed, return noquota */
+ getq_rslt.status = Q_NOQUOTA;
+ } else {
+ gettimeofday(&timev, NULL);
+ getq_rslt.status = Q_OK;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
+ scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
+ DEV_BSIZE * scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
+ dqblk.dqb_bhardlimit / scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
+ dqblk.dqb_bsoftlimit / scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
+ dqblk.dqb_curblocks / scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
+ dqblk.dqb_ihardlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
+ dqblk.dqb_isoftlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
+ dqblk.dqb_curinodes;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
+ dqblk.dqb_btime - timev.tv_sec;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
+ dqblk.dqb_itime - timev.tv_sec;
+ }
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
+ svcerr_systemerr(transp);
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+}
+
+/*
+ * gets the quotas for id, filesystem path.
+ * Return 0 if fail, 1 otherwise
+ */
+static int
+getfsquota(int type, long id, char *path, struct dqblk *dqblk)
+{
+ struct quotafile *qf;
+ /*
+ * Remote quota checking is limited to mounted filesystems.
+ * Since UFS and ZFS support the quota system calls, we
+ * only need to make an fstab object that has the path, and
+ * a blank name for the filesystem type.
+ * This allows the quota_open() call to work the way we
+ * expect it to.
+ *
+ * The static char declaration is because compiler warnings
+ * don't allow passing a const char * to a char *.
+ */
+ int rv;
+ static char blank[] = "";
+ struct fstab fst;
+
+ fst.fs_file = path;
+ fst.fs_mntops = blank;
+ fst.fs_vfstype = blank;
+
+ if (type != USRQUOTA && type != GRPQUOTA)
+ return (0);
+
+ qf = quota_open(&fst, type, O_RDONLY);
+ if (debug)
+ warnx("quota_open(<%s, %s>, %d) returned %p",
+ fst.fs_file, fst.fs_mntops, type,
+ qf);
+ if (qf == NULL)
+ return (0);
+
+ rv = quota_read(qf, dqblk, id) == 0;
+ quota_close(qf);
+ if (debug)
+ warnx("getfsquota(%d, %ld, %s, %p) -> %d",
+ type, id, path, dqblk, rv);
+ return (rv);
+}
diff --git a/libexec/rpc.rstatd/Makefile b/libexec/rpc.rstatd/Makefile
new file mode 100644
index 000000000000..5572748ddf26
--- /dev/null
+++ b/libexec/rpc.rstatd/Makefile
@@ -0,0 +1,10 @@
+PACKAGE= rcmds
+PROG= rpc.rstatd
+SRCS= rstatd.c rstat_proc.c
+MAN= rpc.rstatd.8
+
+LIBADD= rpcsvc devstat
+
+WARNS?= 1
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.rstatd/Makefile.depend b/libexec/rpc.rstatd/Makefile.depend
new file mode 100644
index 000000000000..85bd405d5ac1
--- /dev/null
+++ b/libexec/rpc.rstatd/Makefile.depend
@@ -0,0 +1,20 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/rpc \
+ include/rpcsvc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libdevstat \
+ lib/libkvm \
+ lib/librpcsvc \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rpc.rstatd/rpc.rstatd.8 b/libexec/rpc.rstatd/rpc.rstatd.8
new file mode 100644
index 000000000000..539bc4961b2f
--- /dev/null
+++ b/libexec/rpc.rstatd/rpc.rstatd.8
@@ -0,0 +1,59 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1985, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd June 7, 1993
+.Dt RPC.RSTATD 8
+.Os
+.Sh NAME
+.Nm rpc.rstatd
+.Nd kernel statistics server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rstatd
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is a server which returns performance statistics obtained from the kernel.
+These statistics are read using the
+.Xr rup 1
+command.
+The
+.Nm
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn RPC
+protocol defined in
+.Pa /usr/include/rpcsvc/rstat.x .
+.Sh SEE ALSO
+.Xr rup 1 ,
+.Xr inetd 8
diff --git a/libexec/rpc.rstatd/rstat_proc.c b/libexec/rpc.rstatd/rstat_proc.c
new file mode 100644
index 000000000000..ba4874bda382
--- /dev/null
+++ b/libexec/rpc.rstatd/rstat_proc.c
@@ -0,0 +1,469 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * rstat service: built with rstat.x and derived from rpc.rstatd.c
+ *
+ * Copyright (c) 1984 by Sun Microsystems, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <devstat.h>
+
+#include <net/if.h>
+#include <net/if_mib.h>
+
+#undef FSHIFT /* Use protocol's shift and scale values */
+#undef FSCALE
+#undef if_ipackets
+#undef if_ierrors
+#undef if_opackets
+#undef if_oerrors
+#undef if_collisions
+#include <rpcsvc/rstat.h>
+
+int haveadisk(void);
+void updatexfers(int, int *);
+int stats_service(void);
+
+extern int from_inetd;
+int sincelastreq = 0; /* number of alarms since last request */
+extern int closedown;
+
+union {
+ struct stats s1;
+ struct statsswtch s2;
+ struct statstime s3;
+} stats_all;
+
+void updatestat();
+static int stat_is_init = 0;
+
+static int cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS,
+ CP_IDLE };
+static long bsd_cp_time[CPUSTATES];
+
+
+#ifndef FSCALE
+#define FSCALE (1 << 8)
+#endif
+
+void
+stat_init(void)
+{
+ stat_is_init = 1;
+ alarm(0);
+ updatestat();
+ (void) signal(SIGALRM, updatestat);
+ alarm(1);
+}
+
+statstime *
+rstatproc_stats_3_svc(void *argp, struct svc_req *rqstp)
+{
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return(&stats_all.s3);
+}
+
+statsswtch *
+rstatproc_stats_2_svc(void *argp, struct svc_req *rqstp)
+{
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return(&stats_all.s2);
+}
+
+stats *
+rstatproc_stats_1_svc(void *argp, struct svc_req *rqstp)
+{
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return(&stats_all.s1);
+}
+
+u_int *
+rstatproc_havedisk_3_svc(void *argp, struct svc_req *rqstp)
+{
+ static u_int have;
+
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ have = haveadisk();
+ return(&have);
+}
+
+u_int *
+rstatproc_havedisk_2_svc(void *argp, struct svc_req *rqstp)
+{
+ return(rstatproc_havedisk_3_svc(argp, rqstp));
+}
+
+u_int *
+rstatproc_havedisk_1_svc(void *argp, struct svc_req *rqstp)
+{
+ return(rstatproc_havedisk_3_svc(argp, rqstp));
+}
+
+void
+updatestat(void)
+{
+ int i, hz;
+ struct clockinfo clockrate;
+ struct ifmibdata ifmd;
+ double avrun[3];
+ struct timeval tm, btm;
+ int mib[6];
+ size_t len;
+ uint64_t val;
+ int ifcount;
+
+#ifdef DEBUG
+ fprintf(stderr, "entering updatestat\n");
+#endif
+ if (sincelastreq >= closedown) {
+#ifdef DEBUG
+ fprintf(stderr, "about to closedown\n");
+#endif
+ if (from_inetd)
+ exit(0);
+ else {
+ stat_is_init = 0;
+ return;
+ }
+ }
+ sincelastreq++;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CLOCKRATE;
+ len = sizeof clockrate;
+ if (sysctl(mib, 2, &clockrate, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(kern.clockrate): %m");
+ exit(1);
+ }
+ hz = clockrate.hz;
+
+ len = sizeof(bsd_cp_time);
+ if (sysctlbyname("kern.cp_time", bsd_cp_time, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(kern.cp_time): %m");
+ exit(1);
+ }
+ for(i = 0; i < RSTAT_CPUSTATES ; i++)
+ stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]];
+
+ (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
+
+ stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
+ stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
+ stats_all.s2.avenrun[2] = avrun[2] * FSCALE;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ len = sizeof btm;
+ if (sysctl(mib, 2, &btm, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(kern.boottime): %m");
+ exit(1);
+ }
+
+ stats_all.s2.boottime.tv_sec = btm.tv_sec;
+ stats_all.s2.boottime.tv_usec = btm.tv_usec;
+
+
+#ifdef DEBUG
+ fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0],
+ stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]);
+#endif
+
+#define FETCH_CNT(stat, cnt) do { \
+ len = sizeof(uint64_t); \
+ if (sysctlbyname("vm.stats." #cnt , &val, &len, NULL, 0) < 0) { \
+ syslog(LOG_ERR, "sysctl(vm.stats." #cnt "): %m"); \
+ exit(1); \
+ } \
+ stat = val; \
+} while (0)
+
+ FETCH_CNT(stats_all.s1.v_pgpgin, vm.v_vnodepgsin);
+ FETCH_CNT(stats_all.s1.v_pgpgout, vm.v_vnodepgsout);
+ FETCH_CNT(stats_all.s1.v_pswpin, vm.v_swappgsin);
+ FETCH_CNT(stats_all.s1.v_pswpout, vm.v_swappgsout);
+ FETCH_CNT(stats_all.s1.v_intr, sys.v_intr);
+ FETCH_CNT(stats_all.s2.v_swtch, sys.v_swtch);
+ (void)gettimeofday(&tm, NULL);
+ stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
+ hz*(tm.tv_usec - btm.tv_usec)/1000000;
+
+ /* update disk transfers */
+ updatexfers(RSTAT_DK_NDRIVE, stats_all.s1.dk_xfer);
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_LINK;
+ mib[2] = NETLINK_GENERIC;
+ mib[3] = IFMIB_SYSTEM;
+ mib[4] = IFMIB_IFCOUNT;
+ len = sizeof ifcount;
+ if (sysctl(mib, 5, &ifcount, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(net.link.generic.system.ifcount): %m");
+ exit(1);
+ }
+
+ stats_all.s1.if_ipackets = 0;
+ stats_all.s1.if_opackets = 0;
+ stats_all.s1.if_ierrors = 0;
+ stats_all.s1.if_oerrors = 0;
+ stats_all.s1.if_collisions = 0;
+ for (i = 1; i <= ifcount; i++) {
+ len = sizeof ifmd;
+ mib[3] = IFMIB_IFDATA;
+ mib[4] = i;
+ mib[5] = IFDATA_GENERAL;
+ if (sysctl(mib, 6, &ifmd, &len, 0, 0) < 0) {
+ if (errno == ENOENT)
+ continue;
+
+ syslog(LOG_ERR, "sysctl(net.link.ifdata.%d.general)"
+ ": %m", i);
+ exit(1);
+ }
+
+ stats_all.s1.if_ipackets += ifmd.ifmd_data.ifi_ipackets;
+ stats_all.s1.if_opackets += ifmd.ifmd_data.ifi_opackets;
+ stats_all.s1.if_ierrors += ifmd.ifmd_data.ifi_ierrors;
+ stats_all.s1.if_oerrors += ifmd.ifmd_data.ifi_oerrors;
+ stats_all.s1.if_collisions += ifmd.ifmd_data.ifi_collisions;
+ }
+ (void)gettimeofday(&tm, NULL);
+ stats_all.s3.curtime.tv_sec = tm.tv_sec;
+ stats_all.s3.curtime.tv_usec = tm.tv_usec;
+ alarm(1);
+}
+
+/*
+ * returns true if have a disk
+ */
+int
+haveadisk(void)
+{
+ register int i;
+ struct statinfo stats;
+ int num_devices, retval = 0;
+
+ if ((num_devices = devstat_getnumdevs(NULL)) < 0) {
+ syslog(LOG_ERR, "rstatd: can't get number of devices: %s",
+ devstat_errbuf);
+ exit(1);
+ }
+
+ if (devstat_checkversion(NULL) < 0) {
+ syslog(LOG_ERR, "rstatd: %s", devstat_errbuf);
+ exit(1);
+ }
+
+ stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ bzero(stats.dinfo, sizeof(struct devinfo));
+
+ if (devstat_getdevs(NULL, &stats) == -1) {
+ syslog(LOG_ERR, "rstatd: can't get device list: %s",
+ devstat_errbuf);
+ exit(1);
+ }
+ for (i = 0; i < stats.dinfo->numdevs; i++) {
+ if (((stats.dinfo->devices[i].device_type
+ & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT)
+ && ((stats.dinfo->devices[i].device_type
+ & DEVSTAT_TYPE_PASS) == 0)) {
+ retval = 1;
+ break;
+ }
+ }
+
+ if (stats.dinfo->mem_ptr)
+ free(stats.dinfo->mem_ptr);
+
+ free(stats.dinfo);
+ return(retval);
+}
+
+void
+updatexfers(int numdevs, int *devs)
+{
+ register int i, j, k, t;
+ struct statinfo stats;
+ int num_devices = 0;
+ u_int64_t total_transfers;
+
+ if ((num_devices = devstat_getnumdevs(NULL)) < 0) {
+ syslog(LOG_ERR, "rstatd: can't get number of devices: %s",
+ devstat_errbuf);
+ exit(1);
+ }
+
+ if (devstat_checkversion(NULL) < 0) {
+ syslog(LOG_ERR, "rstatd: %s", devstat_errbuf);
+ exit(1);
+ }
+
+ stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ bzero(stats.dinfo, sizeof(struct devinfo));
+
+ if (devstat_getdevs(NULL, &stats) == -1) {
+ syslog(LOG_ERR, "rstatd: can't get device list: %s",
+ devstat_errbuf);
+ exit(1);
+ }
+
+ for (i = 0, j = 0; i < stats.dinfo->numdevs && j < numdevs; i++) {
+ if (((stats.dinfo->devices[i].device_type
+ & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT)
+ && ((stats.dinfo->devices[i].device_type
+ & DEVSTAT_TYPE_PASS) == 0)) {
+ total_transfers = 0;
+ for (k = 0; k < DEVSTAT_N_TRANS_FLAGS; k++)
+ total_transfers +=
+ stats.dinfo->devices[i].operations[k];
+ /*
+ * XXX KDM If the total transfers for this device
+ * are greater than the amount we can fit in a
+ * signed integer, just set them to the maximum
+ * amount we can fit in a signed integer. I have a
+ * feeling that the rstat protocol assumes 32-bit
+ * integers, so this could well break on a 64-bit
+ * architecture like the Alpha.
+ */
+ if (total_transfers > INT_MAX)
+ t = INT_MAX;
+ else
+ t = total_transfers;
+ devs[j] = t;
+ j++;
+ }
+ }
+
+ if (stats.dinfo->mem_ptr)
+ free(stats.dinfo->mem_ptr);
+
+ free(stats.dinfo);
+}
+
+void
+rstat_service(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ int fill;
+ } argument;
+ void *result;
+ xdrproc_t xdr_argument, xdr_result;
+ typedef void *(svc_cb)(void *arg, struct svc_req *rqstp);
+ svc_cb *local;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
+ goto leave;
+
+ case RSTATPROC_STATS:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_statstime;
+ switch (rqstp->rq_vers) {
+ case RSTATVERS_ORIG:
+ local = (svc_cb *)rstatproc_stats_1_svc;
+ break;
+ case RSTATVERS_SWTCH:
+ local = (svc_cb *)rstatproc_stats_2_svc;
+ break;
+ case RSTATVERS_TIME:
+ local = (svc_cb *)rstatproc_stats_3_svc;
+ break;
+ default:
+ svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ case RSTATPROC_HAVEDISK:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_u_int;
+ switch (rqstp->rq_vers) {
+ case RSTATVERS_ORIG:
+ local = (svc_cb *)rstatproc_havedisk_1_svc;
+ break;
+ case RSTATVERS_SWTCH:
+ local = (svc_cb *)rstatproc_havedisk_2_svc;
+ break;
+ case RSTATVERS_TIME:
+ local = (svc_cb *)rstatproc_havedisk_3_svc;
+ break;
+ default:
+ svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL &&
+ !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, &argument))
+ errx(1, "unable to free arguments");
+leave:
+ if (from_inetd)
+ exit(0);
+}
diff --git a/libexec/rpc.rstatd/rstatd.c b/libexec/rpc.rstatd/rstatd.c
new file mode 100644
index 000000000000..7cc3bac71c5d
--- /dev/null
+++ b/libexec/rpc.rstatd/rstatd.c
@@ -0,0 +1,126 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1993, John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <signal.h>
+#include <syslog.h>
+#include <rpcsvc/rstat.h>
+
+extern void rstat_service(struct svc_req *, SVCXPRT *);
+
+int from_inetd = 1; /* started from inetd ? */
+int closedown = 20; /* how long to wait before going dormant */
+
+void
+cleanup(int sig __unused)
+{
+ (void) rpcb_unset(RSTATPROG, RSTATVERS_TIME, NULL);
+ (void) rpcb_unset(RSTATPROG, RSTATVERS_SWTCH, NULL);
+ (void) rpcb_unset(RSTATPROG, RSTATVERS_ORIG, NULL);
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ SVCXPRT *transp;
+ int ok;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ if (argc == 2)
+ closedown = atoi(argv[1]);
+ if (closedown <= 0)
+ closedown = 20;
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void)rpcb_unset(RSTATPROG, RSTATVERS_TIME, NULL);
+ (void)rpcb_unset(RSTATPROG, RSTATVERS_SWTCH, NULL);
+ (void)rpcb_unset(RSTATPROG, RSTATVERS_ORIG, NULL);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rstatd", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ ok = svc_reg(transp, RSTATPROG, RSTATVERS_TIME,
+ rstat_service, NULL);
+ } else
+ ok = svc_create(rstat_service,
+ RSTATPROG, RSTATVERS_TIME, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_TIME, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+ if (from_inetd)
+ ok = svc_reg(transp, RSTATPROG, RSTATVERS_SWTCH,
+ rstat_service, NULL);
+ else
+ ok = svc_create(rstat_service,
+ RSTATPROG, RSTATVERS_SWTCH, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_SWTCH, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+ if (from_inetd)
+ ok = svc_reg(transp, RSTATPROG, RSTATVERS_ORIG,
+ rstat_service, NULL);
+ else
+ ok = svc_create(rstat_service,
+ RSTATPROG, RSTATVERS_ORIG, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_ORIG, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
diff --git a/libexec/rpc.rusersd/Makefile b/libexec/rpc.rusersd/Makefile
new file mode 100644
index 000000000000..f59002e4b5fd
--- /dev/null
+++ b/libexec/rpc.rusersd/Makefile
@@ -0,0 +1,13 @@
+PACKAGE= rcmds
+PROG= rpc.rusersd
+SRCS= rusersd.c rusers_proc.c extern.h
+MAN= rpc.rusersd.8
+
+LIBADD= rpcsvc
+
+#.if exists(/usr/X11R6/include/X11/extensions/xidle.h)
+#CFLAGS+= -DXIDLE
+#LDADD+= -L/usr/X11R6/lib -lXext -lX11
+#.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.rusersd/Makefile.depend b/libexec/rpc.rusersd/Makefile.depend
new file mode 100644
index 000000000000..352a225b19c6
--- /dev/null
+++ b/libexec/rpc.rusersd/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/rpc \
+ include/rpcsvc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/librpcsvc \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rpc.rusersd/extern.h b/libexec/rpc.rusersd/extern.h
new file mode 100644
index 000000000000..3102740459c1
--- /dev/null
+++ b/libexec/rpc.rusersd/extern.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1993, John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern int from_inetd;
+
+void rusers_service(struct svc_req *, SVCXPRT *);
diff --git a/libexec/rpc.rusersd/rpc.rusersd.8 b/libexec/rpc.rusersd/rpc.rusersd.8
new file mode 100644
index 000000000000..54fbb4c561ab
--- /dev/null
+++ b/libexec/rpc.rusersd/rpc.rusersd.8
@@ -0,0 +1,62 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1985, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd June 7, 1993
+.Dt RPC.RUSERSD 8
+.Os
+.Sh NAME
+.Nm rpc.rusersd
+.Nd logged in users server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rusersd
+.Sh DESCRIPTION
+The
+.Nm
+utility is a server which returns information about users
+currently logged in to the system.
+.Pp
+The currently logged in users are queried using the
+.Xr rusers 1
+command.
+The
+.Nm
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn RPC
+protocol defined in
+.Pa /usr/include/rpcsvc/rnusers.x .
+.Sh SEE ALSO
+.Xr rusers 1 ,
+.Xr w 1 ,
+.Xr who 1 ,
+.Xr inetd 8
diff --git a/libexec/rpc.rusersd/rusers_proc.c b/libexec/rpc.rusersd/rusers_proc.c
new file mode 100644
index 000000000000..3bc4169a989f
--- /dev/null
+++ b/libexec/rpc.rusersd/rusers_proc.c
@@ -0,0 +1,329 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1993, John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef DEBUG
+#include <errno.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <utmpx.h>
+#ifdef XIDLE
+#include <setjmp.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/xidle.h>
+#endif
+#include <rpcsvc/rnusers.h>
+
+#include "extern.h"
+
+#ifndef _PATH_DEV
+#define _PATH_DEV "/dev"
+#endif
+
+static utmpidle utmp_idle[MAXUSERS];
+static utmp old_utmp[MAXUSERS];
+static struct utmpx utmp_list[MAXUSERS];
+
+#ifdef XIDLE
+static Display *dpy;
+
+static jmp_buf openAbort;
+
+static void
+abortOpen(void)
+{
+ longjmp (openAbort, 1);
+}
+
+XqueryIdle(char *display)
+{
+ int first_event, first_error;
+ Time IdleTime;
+
+ (void) signal (SIGALRM, abortOpen);
+ (void) alarm ((unsigned) 10);
+ if (!setjmp (openAbort)) {
+ if (!(dpy= XOpenDisplay(display))) {
+ syslog(LOG_ERR, "Cannot open display %s", display);
+ return(-1);
+ }
+ if (XidleQueryExtension(dpy, &first_event, &first_error)) {
+ if (!XGetIdleTime(dpy, &IdleTime)) {
+ syslog(LOG_ERR, "%s: unable to get idle time", display);
+ return(-1);
+ }
+ } else {
+ syslog(LOG_ERR, "%s: Xidle extension not loaded", display);
+ return(-1);
+ }
+ XCloseDisplay(dpy);
+ } else {
+ syslog(LOG_ERR, "%s: server grabbed for over 10 seconds", display);
+ return(-1);
+ }
+ (void) signal (SIGALRM, SIG_DFL);
+ (void) alarm ((unsigned) 0);
+
+ IdleTime /= 1000;
+ return((IdleTime + 30) / 60);
+}
+#endif
+
+static u_int
+getidle(const char *tty, const char *display __unused)
+{
+ struct stat st;
+ char ttyname[PATH_MAX];
+ time_t now;
+ u_long idle;
+
+ /*
+ * If this is an X terminal or console, then try the
+ * XIdle extension
+ */
+#ifdef XIDLE
+ if (display && *display && (idle = XqueryIdle(display)) >= 0)
+ return(idle);
+#endif
+ idle = 0;
+ if (*tty == 'X') {
+ u_long kbd_idle, mouse_idle;
+#if !defined(__FreeBSD__)
+ kbd_idle = getidle("kbd", NULL);
+#else
+ kbd_idle = getidle("vga", NULL);
+#endif
+ mouse_idle = getidle("mouse", NULL);
+ idle = (kbd_idle < mouse_idle)?kbd_idle:mouse_idle;
+ } else {
+ sprintf(ttyname, "%s/%s", _PATH_DEV, tty);
+ if (stat(ttyname, &st) < 0) {
+#ifdef DEBUG
+ printf("%s: %s\n", ttyname, strerror(errno));
+#endif
+ return(-1);
+ }
+ time(&now);
+#ifdef DEBUG
+ printf("%s: now=%d atime=%d\n", ttyname, now,
+ st.st_atime);
+#endif
+ idle = now - st.st_atime;
+ idle = (idle + 30) / 60; /* secs->mins */
+ }
+
+ return(idle);
+}
+
+static utmpidlearr *
+do_names_2(void)
+{
+ static utmpidlearr ut;
+ struct utmpx *usr;
+ int nusers = 0;
+
+ memset(&ut, 0, sizeof(ut));
+ ut.utmpidlearr_val = &utmp_idle[0];
+
+ setutxent();
+ while ((usr = getutxent()) != NULL && nusers < MAXUSERS) {
+ if (usr->ut_type != USER_PROCESS)
+ continue;
+
+ memcpy(&utmp_list[nusers], usr, sizeof(*usr));
+ utmp_idle[nusers].ui_utmp.ut_time = usr->ut_tv.tv_sec;
+ utmp_idle[nusers].ui_idle =
+ getidle(usr->ut_line, usr->ut_host);
+ utmp_idle[nusers].ui_utmp.ut_line =
+ utmp_list[nusers].ut_line;
+ utmp_idle[nusers].ui_utmp.ut_name =
+ utmp_list[nusers].ut_user;
+ utmp_idle[nusers].ui_utmp.ut_host =
+ utmp_list[nusers].ut_host;
+
+ nusers++;
+ }
+ endutxent();
+
+ ut.utmpidlearr_len = nusers;
+ return(&ut);
+}
+
+static int *
+rusers_num(void *argp __unused, struct svc_req *rqstp __unused)
+{
+ static int num_users = 0;
+ struct utmpx *usr;
+
+ setutxent();
+ while ((usr = getutxent()) != NULL) {
+ if (usr->ut_type != USER_PROCESS)
+ continue;
+ num_users++;
+ }
+ endutxent();
+
+ return(&num_users);
+}
+
+static utmparr *
+do_names_1(void)
+{
+ utmpidlearr *utidle;
+ static utmparr ut;
+ unsigned int i;
+
+ bzero((char *)&ut, sizeof(ut));
+
+ utidle = do_names_2();
+ if (utidle) {
+ ut.utmparr_len = utidle->utmpidlearr_len;
+ ut.utmparr_val = &old_utmp[0];
+ for (i = 0; i < ut.utmparr_len; i++)
+ bcopy(&utmp_idle[i].ui_utmp, &old_utmp[i],
+ sizeof(old_utmp[0]));
+
+ }
+
+ return(&ut);
+}
+
+utmpidlearr *
+rusersproc_names_2_svc(void *argp __unused, struct svc_req *rqstp __unused)
+{
+
+ return (do_names_2());
+}
+
+utmpidlearr *
+rusersproc_allnames_2_svc(void *argp __unused, struct svc_req *rqstp __unused)
+{
+
+ return (do_names_2());
+}
+
+utmparr *
+rusersproc_names_1_svc(void *argp __unused, struct svc_req *rqstp __unused)
+{
+
+ return (do_names_1());
+}
+
+utmparr *
+rusersproc_allnames_1_svc(void *argp __unused, struct svc_req *rqstp __unused)
+{
+
+ return (do_names_1());
+}
+
+typedef void *(*rusersproc_t)(void *, struct svc_req *);
+
+void
+rusers_service(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ int fill;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ rusersproc_t local;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
+ goto leave;
+
+ case RUSERSPROC_NUM:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_int;
+ local = (rusersproc_t)rusers_num;
+ break;
+
+ case RUSERSPROC_NAMES:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_utmpidlearr;
+ switch (rqstp->rq_vers) {
+ case RUSERSVERS_ORIG:
+ local = (rusersproc_t)rusersproc_names_1_svc;
+ break;
+ case RUSERSVERS_IDLE:
+ local = (rusersproc_t)rusersproc_names_2_svc;
+ break;
+ default:
+ svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ case RUSERSPROC_ALLNAMES:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_utmpidlearr;
+ switch (rqstp->rq_vers) {
+ case RUSERSVERS_ORIG:
+ local = (rusersproc_t)rusersproc_allnames_1_svc;
+ break;
+ case RUSERSVERS_IDLE:
+ local = (rusersproc_t)rusersproc_allnames_2_svc;
+ break;
+ default:
+ svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero(&argument, sizeof(argument));
+ if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL &&
+ !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, &argument)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+leave:
+ if (from_inetd)
+ exit(0);
+}
diff --git a/libexec/rpc.rusersd/rusersd.c b/libexec/rpc.rusersd/rusersd.c
new file mode 100644
index 000000000000..cf00dd8d181e
--- /dev/null
+++ b/libexec/rpc.rusersd/rusersd.c
@@ -0,0 +1,109 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1993, John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <syslog.h>
+#include <rpcsvc/rnusers.h>
+
+#include "extern.h"
+
+int from_inetd = 1;
+
+static void
+cleanup(int sig __unused)
+{
+ (void) rpcb_unset(RUSERSPROG, RUSERSVERS_IDLE, NULL);
+ (void) rpcb_unset(RUSERSPROG, RUSERSVERS_ORIG, NULL);
+ exit(0);
+}
+
+int
+main(int argc __unused, char *argv[] __unused)
+{
+ SVCXPRT *transp = NULL; /* Keep compiler happy. */
+ int ok;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void) rpcb_unset(RUSERSPROG, RUSERSVERS_IDLE, NULL);
+ (void) rpcb_unset(RUSERSPROG, RUSERSVERS_ORIG, NULL);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rusersd", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ ok = svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE,
+ rusers_service, NULL);
+ } else
+ ok = svc_create(rusers_service,
+ RUSERSPROG, RUSERSVERS_IDLE, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_IDLE, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+ if (from_inetd)
+ ok = svc_reg(transp, RUSERSPROG, RUSERSVERS_ORIG,
+ rusers_service, NULL);
+ else
+ ok = svc_create(rusers_service,
+ RUSERSPROG, RUSERSVERS_ORIG, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_ORIG, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
diff --git a/libexec/rpc.rwalld/Makefile b/libexec/rpc.rwalld/Makefile
new file mode 100644
index 000000000000..8d10a91fffd0
--- /dev/null
+++ b/libexec/rpc.rwalld/Makefile
@@ -0,0 +1,10 @@
+PACKAGE= rcmds
+PROG= rpc.rwalld
+SRCS= rwalld.c
+MAN= rpc.rwalld.8
+
+LIBADD= util
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.rwalld/Makefile.depend b/libexec/rpc.rwalld/Makefile.depend
new file mode 100644
index 000000000000..7e5c47e39608
--- /dev/null
+++ b/libexec/rpc.rwalld/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/rpc \
+ include/rpcsvc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rpc.rwalld/rpc.rwalld.8 b/libexec/rpc.rwalld/rpc.rwalld.8
new file mode 100644
index 000000000000..e5e524b1a764
--- /dev/null
+++ b/libexec/rpc.rwalld/rpc.rwalld.8
@@ -0,0 +1,77 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1985, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd June 7, 1993
+.Dt RPC.RWALLD 8
+.Os
+.Sh NAME
+.Nm rpc.rwalld
+.Nd write messages to users currently logged in server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rwalld
+.Op Fl n
+.Sh DESCRIPTION
+The
+.Nm
+utility is a server which will send a message to users
+currently logged in to the system.
+This server
+invokes the
+.Xr wall 1
+command to actually write the messages to the
+system.
+.Pp
+The following option is available:
+.Bl -tag -width indent
+.It Fl n
+Do not become a daemon.
+This option is only available when
+.Nm
+is not invoked by
+.Xr inetd 8 .
+.El
+.Pp
+Messages are sent to this server by the
+.Xr rwall 1
+command.
+The
+.Nm
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn RPC
+protocol defined in
+.Pa /usr/include/rpcsvc/rwall.x .
+.Sh SEE ALSO
+.Xr rwall 1 ,
+.Xr wall 1 ,
+.Xr inetd 8
diff --git a/libexec/rpc.rwalld/rwalld.c b/libexec/rpc.rwalld/rwalld.c
new file mode 100644
index 000000000000..f265ff034e9c
--- /dev/null
+++ b/libexec/rpc.rwalld/rwalld.c
@@ -0,0 +1,205 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <err.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rwall.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#ifdef OSF
+#define WALL_CMD "/usr/sbin/wall"
+#else
+#define WALL_CMD "/usr/bin/wall -n"
+#endif
+
+void wallprog_1(struct svc_req *rqstp, SVCXPRT *transp);
+void possess(void);
+void killkids(int sig);
+static void usage(void) __dead2;
+
+int nodaemon = 0;
+int from_inetd = 1;
+
+int
+main(int argc, char *argv[])
+{
+ SVCXPRT *transp;
+ socklen_t salen;
+ int ok;
+ struct sockaddr_storage sa;
+
+ if (argc == 2 && !strcmp(argv[1], "-n"))
+ nodaemon = 1;
+ if (argc != 1 && !nodaemon)
+ usage();
+
+ if (geteuid() == 0) {
+ struct passwd *pep = getpwnam("nobody");
+ if (pep)
+ setuid(pep->pw_uid);
+ else
+ setuid(getuid());
+ }
+
+ /*
+ * See if inetd started us
+ */
+ salen = sizeof(sa);
+ if (getsockname(0, (struct sockaddr *)&sa, &salen) < 0) {
+ from_inetd = 0;
+ }
+
+ if (!from_inetd) {
+ if (!nodaemon)
+ possess();
+
+ (void)rpcb_unset(WALLPROG, WALLVERS, NULL);
+ }
+
+ (void)signal(SIGCHLD, killkids);
+
+ openlog("rpc.rwalld", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ /* create and register the service */
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "couldn't create udp service.");
+ exit(1);
+ }
+ ok = svc_reg(transp, WALLPROG, WALLVERS,
+ wallprog_1, NULL);
+ } else
+ ok = svc_create(wallprog_1,
+ WALLPROG, WALLVERS, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (WALLPROG, WALLVERS, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: rpc.rwalld [-n]\n");
+ exit(1);
+}
+
+void
+possess(void)
+{
+ daemon(0, 0);
+}
+
+void
+killkids(int sig __unused)
+{
+ while(wait4(-1, NULL, WNOHANG, NULL) > 0)
+ ;
+}
+
+void *
+wallproc_wall_1_svc(wrapstring *s, struct svc_req *rqstp __unused)
+{
+ static void *dummy = NULL;
+
+ /* fork, popen wall with special option, and send the message */
+ if (fork() == 0) {
+ FILE *pfp;
+
+ pfp = popen(WALL_CMD, "w");
+ if (pfp != NULL) {
+ fprintf(pfp, "\007\007%s", *s);
+ pclose(pfp);
+ exit(0);
+ }
+ }
+ return(&dummy);
+}
+
+void
+wallprog_1(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ char *wallproc_wall_1_arg;
+ } argument;
+ void *result;
+ xdrproc_t xdr_argument, xdr_result;
+ typedef void *(svc_cb)(void *arg, struct svc_req *rqstp);
+ svc_cb *local;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
+ goto leave;
+
+ case WALLPROC_WALL:
+ xdr_argument = (xdrproc_t)xdr_wrapstring;
+ xdr_result = (xdrproc_t)xdr_void;
+ local = (svc_cb *)wallproc_wall_1_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero(&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL &&
+ !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, &argument)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+leave:
+ if (from_inetd)
+ exit(0);
+}
diff --git a/libexec/rpc.sprayd/Makefile b/libexec/rpc.sprayd/Makefile
new file mode 100644
index 000000000000..ed4b25615b09
--- /dev/null
+++ b/libexec/rpc.sprayd/Makefile
@@ -0,0 +1,7 @@
+PROG = rpc.sprayd
+SRCS = sprayd.c
+MAN = rpc.sprayd.8
+
+LIBADD= rpcsvc
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.sprayd/Makefile.depend b/libexec/rpc.sprayd/Makefile.depend
new file mode 100644
index 000000000000..352a225b19c6
--- /dev/null
+++ b/libexec/rpc.sprayd/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/rpc \
+ include/rpcsvc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/librpcsvc \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rpc.sprayd/rpc.sprayd.8 b/libexec/rpc.sprayd/rpc.sprayd.8
new file mode 100644
index 000000000000..09cdf1560e91
--- /dev/null
+++ b/libexec/rpc.sprayd/rpc.sprayd.8
@@ -0,0 +1,54 @@
+.\" $NetBSD: rpc.sprayd.8,v 1.10 2009/10/21 01:07:46 snj Exp $
+.\"
+.\" Copyright (c) 1994 Christos Zoulas
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd December 27, 2017
+.Dt RPC.SPRAYD 8
+.Os
+.Sh NAME
+.Nm rpc.sprayd ,
+.Nm sprayd
+.Nd spray server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.sprayd
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is a server which records packets sent by the
+.Xr spray 8
+command and sends a traffic report to the originator of the packets.
+The
+.Nm
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn RPC
+protocol defined in
+.Pa /usr/include/rpcsvc/spray.x .
+.Sh SEE ALSO
+.Xr spray 8
diff --git a/libexec/rpc.sprayd/sprayd.c b/libexec/rpc.sprayd/sprayd.c
new file mode 100644
index 000000000000..2a71a93bf4ef
--- /dev/null
+++ b/libexec/rpc.sprayd/sprayd.c
@@ -0,0 +1,160 @@
+/* $NetBSD: sprayd.c,v 1.15 2009/10/21 01:07:46 snj Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1994 Christos Zoulas
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rpc/rpc.h>
+#include <rpcsvc/spray.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <syslog.h>
+#include <unistd.h>
+
+static void spray_service(struct svc_req *, SVCXPRT *);
+
+static int from_inetd = 1;
+
+#define timersub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+
+#define TIMEOUT 120
+
+static void
+cleanup(int sig __unused)
+{
+ (void)rpcb_unset(SPRAYPROG, SPRAYVERS, NULL);
+ exit(0);
+}
+
+static void
+die(int sig __unused)
+{
+ exit(0);
+}
+
+int
+main(int argc __unused, char *argv[] __unused)
+{
+ SVCXPRT *transp;
+ int ok;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void)rpcb_unset(SPRAYPROG, SPRAYVERS, NULL);
+
+ (void)signal(SIGINT, cleanup);
+ (void)signal(SIGTERM, cleanup);
+ (void)signal(SIGHUP, cleanup);
+ } else {
+ (void)signal(SIGALRM, die);
+ alarm(TIMEOUT);
+ }
+
+ openlog("rpc.sprayd", LOG_PID, LOG_DAEMON);
+
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ ok = svc_reg(transp, SPRAYPROG, SPRAYVERS,
+ spray_service, NULL);
+ } else
+ ok = svc_create(spray_service,
+ SPRAYPROG, SPRAYVERS, "udp");
+ if (!ok) {
+ syslog(LOG_ERR,
+ "unable to register (SPRAYPROG, SPRAYVERS, %s)",
+ (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ return 1;
+}
+
+
+static void
+spray_service(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ static spraycumul scum;
+ static struct timeval clear, get;
+
+ switch (rqstp->rq_proc) {
+ case SPRAYPROC_CLEAR:
+ scum.counter = 0;
+ (void)gettimeofday(&clear, 0);
+ /*FALLTHROUGH*/
+
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
+ return;
+
+ case SPRAYPROC_SPRAY:
+ scum.counter++;
+ return;
+
+ case SPRAYPROC_GET:
+ (void)gettimeofday(&get, 0);
+ timersub(&get, &clear, &get);
+ scum.clock.sec = get.tv_sec;
+ scum.clock.usec = get.tv_usec;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_spraycumul, &scum)) {
+ svcerr_systemerr(transp);
+ syslog(LOG_WARNING, "bad svc_sendreply");
+ }
+}
diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile
new file mode 100644
index 000000000000..b6ff7681e658
--- /dev/null
+++ b/libexec/rtld-elf/Makefile
@@ -0,0 +1,134 @@
+# Use the following command to build local debug version of dynamic
+# linker:
+# make DEBUG_FLAGS=-g WITHOUT_TESTS=yes all
+
+RTLD_ELF_DIR:= ${.PARSEDIR}
+
+.include <src.opts.mk>
+PACKAGE= clibs
+MK_PIE= no # Always position independent using local rules
+# Not compatible with sanitizer instrumentation or SSP.
+MK_ASAN= no
+MK_SSP= no
+MK_UBSAN= no
+
+.include <bsd.compat.pre.mk>
+
+# SSP forced off already implies FORTIFY_SOURCE=0, but we must make sure that
+# one cannot turn it back on.
+FORTIFY_SOURCE= 0
+
+.if !defined(NEED_COMPAT)
+CONFS= libmap.conf
+.endif
+PROG?= ld-elf.so.1
+.for _libcompat in ${_ALL_libcompats}
+.if ${PROG:M*ld-elf${_libcompat}[-.]*} != ""
+TAGS+= lib${_libcompat}
+.endif
+.endfor
+SRCS= \
+ crtbrand.S \
+ rtld_start.S \
+ reloc.c \
+ rtld.c \
+ rtld_lock.c \
+ rtld_malloc.c \
+ rtld_printf.c \
+ map_object.c \
+ xmalloc.c \
+ debug.c \
+ libmap.c
+MAN?= rtld.1
+ACFLAGS+= -DLOCORE
+CFLAGS+= -Wall -DIN_RTLD -ffreestanding
+CFLAGS+= -I${SRCTOP}/lib/csu/common
+.if exists(${RTLD_ELF_DIR}/${MACHINE_ARCH:S/powerpc64le/powerpc64/})
+RTLD_ARCH= ${MACHINE_ARCH:S/powerpc64le/powerpc64/}
+.else
+RTLD_ARCH= ${MACHINE_CPUARCH}
+.endif
+CFLAGS+= -I${RTLD_ELF_DIR}/${RTLD_ARCH} -I${RTLD_ELF_DIR}
+
+NO_WCAST_ALIGN= yes
+INSTALLFLAGS= -C -b
+PRECIOUSPROG=
+BINDIR= /libexec
+SYMLINKS= ../..${BINDIR}/${PROG} ${LIBEXECDIR}/${PROG}
+MLINKS?= rtld.1 ld-elf.so.1.1 \
+ rtld.1 ld.so.1
+
+CFLAGS+= -fpic -DPIC $(DEBUG)
+
+LDFLAGS+= -shared -Wl,-Bsymbolic -Wl,-z,defs -nostdlib -e ${RTLD_ENTRY}
+# Pull in the dependencies that we use from libc
+.include "rtld-libc/Makefile.inc"
+
+VERSION_DEF= ${LIBCSRCDIR}/Versions.def
+SYMBOL_MAPS= ${RTLD_ELF_DIR}/Symbol.map
+VERSION_MAP= Version.map
+LDFLAGS+= -Wl,--version-script=${VERSION_MAP}
+
+.if exists(${RTLD_ELF_DIR}/${RTLD_ARCH}/Symbol.map)
+SYMBOL_MAPS+= ${RTLD_ELF_DIR}/${RTLD_ARCH}/Symbol.map
+.endif
+
+.sinclude "${RTLD_ELF_DIR}/${RTLD_ARCH}/Makefile.inc"
+RTLD_ENTRY?= .rtld_start
+
+# Always produce the map file so that may be inspected to confirm
+# undesired code is not linked from libsys/libc.
+MAPFILE= ld-elf.so.1.map
+LDFLAGS+= -Wl,-Map=${MAPFILE} -Wl,--cref
+CLEANFILES+= ${MAPFILE}
+
+afterbuild:
+ @if grep __libsys_interposing ${MAPFILE} >/dev/null ; then \
+ echo "libsys_interposing leaked" 1>&2 ; \
+ exit 1 ; \
+ fi
+ @if grep __libc_interposing ${MAPFILE} >/dev/null ; then \
+ echo "libc_interposing leaked" 1>&2 ; \
+ exit 1 ; \
+ fi
+ @if grep xlocale ${MAPFILE} >/dev/null ; then \
+ echo "xlocale leaked" 1>&2 ; \
+ exit 1 ; \
+ fi
+ @if grep fprintf ${MAPFILE} >/dev/null ; then \
+ echo "stdio leaked" 1>&2 ; \
+ exit 1 ; \
+ fi
+
+
+# Since moving rtld-elf to /libexec, we need to create a symlink.
+# Fixup the existing binary that's there so we can symlink over it.
+beforeinstall:
+.if exists(${DESTDIR}/usr/libexec/${PROG}) && ${MK_STAGING} == "no"
+ -chflags -h noschg ${DESTDIR}/usr/libexec/${PROG}
+.endif
+
+.PATH: ${RTLD_ELF_DIR}/${RTLD_ARCH} ${SRCTOP}/lib/csu/common
+
+.if ${.CURDIR} == ${RTLD_ELF_DIR}
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+.endif
+
+# Some of the required math functions (div & mod) are implemented in
+# libcompiler_rt on some architectures.
+LIBADD+= compiler_rt
+
+.include <bsd.prog.mk>
+${PROG_FULL}: ${VERSION_MAP}
+.include <bsd.symver.mk>
+
+.if ${COMPILER_TYPE} == "gcc"
+# GCC warns about redeclarations even though they have __exported
+# and are therefore not identical to the ones from the system headers.
+CFLAGS+= -Wno-redundant-decls
+.endif
+
+# Add dependencies on libc and libsys archives after bsd.prog.mk
+# includes bsd.libnames.mk so they are defined.
+rtld_libc.a: ${LIBC_NOSSP_PIC} ${LIBSYS_PIC}
diff --git a/libexec/rtld-elf/Makefile.depend b/libexec/rtld-elf/Makefile.depend
new file mode 100644
index 000000000000..bcfe960e8abe
--- /dev/null
+++ b/libexec/rtld-elf/Makefile.depend
@@ -0,0 +1,15 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/ssp \
+ include/xlocale \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rtld-elf/Symbol.map b/libexec/rtld-elf/Symbol.map
new file mode 100644
index 000000000000..9e9e702a1261
--- /dev/null
+++ b/libexec/rtld-elf/Symbol.map
@@ -0,0 +1,43 @@
+/*
+ */
+
+FBSD_1.0 {
+ _rtld_error;
+ dlclose;
+ dlerror;
+ dlopen;
+ dlsym;
+ dlfunc;
+ dlvsym;
+ dladdr;
+ dllockinit;
+ dlinfo;
+ dl_iterate_phdr;
+ r_debug_state;
+ __tls_get_addr;
+};
+
+FBSD_1.3 {
+ fdlopen;
+};
+
+FBSD_1.8 {
+ rtld_get_var;
+ rtld_set_var;
+};
+
+FBSDprivate_1.0 {
+ _dl_iterate_phdr_locked;
+ _rtld_thread_init;
+ _rtld_allocate_tls;
+ _rtld_free_tls;
+ _rtld_atfork_pre;
+ _rtld_atfork_post;
+ _rtld_addr_phdr;
+ _rtld_get_stack_prot;
+ _rtld_is_dlopened;
+ _r_debug_postinit;
+ _rtld_version__FreeBSD_version;
+ _rtld_version_laddr_offset;
+ _rtld_version_dlpi_tls_data;
+};
diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c
new file mode 100644
index 000000000000..62d664f8fb80
--- /dev/null
+++ b/libexec/rtld-elf/aarch64/reloc.c
@@ -0,0 +1,626 @@
+/*-
+ * Copyright (c) 2014-2015 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Andrew Turner
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <machine/sysarch.h>
+
+#include <stdlib.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "rtld_printf.h"
+
+/*
+ * This is not the correct prototype, but we only need it for
+ * a function pointer to a simple asm function.
+ */
+void *_rtld_tlsdesc_static(void *);
+void *_rtld_tlsdesc_undef(void *);
+void *_rtld_tlsdesc_dynamic(void *);
+
+void _exit(int);
+
+bool
+arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp)
+{
+ if (dynp->d_tag == DT_AARCH64_VARIANT_PCS) {
+ obj->variant_pcs = true;
+ return (true);
+ }
+
+ return (false);
+}
+
+bool
+arch_digest_note(struct Struct_Obj_Entry *obj __unused, const Elf_Note *note)
+{
+ const char *note_name;
+ const uint32_t *note_data;
+
+ note_name = (const char *)(note + 1);
+ /* Only handle GNU notes */
+ if (note->n_namesz != sizeof(ELF_NOTE_GNU) ||
+ strncmp(note_name, ELF_NOTE_GNU, sizeof(ELF_NOTE_GNU)) != 0)
+ return (false);
+
+ /* Only handle GNU property notes */
+ if (note->n_type != NT_GNU_PROPERTY_TYPE_0)
+ return (false);
+
+ /*
+ * note_data[0] - Type
+ * note_data[1] - Length
+ * note_data[2] - Data
+ * note_data[3] - Padding?
+ */
+ note_data = (const uint32_t *)(note_name + note->n_namesz);
+
+ /* Only handle AArch64 feature notes */
+ if (note_data[0] != GNU_PROPERTY_AARCH64_FEATURE_1_AND)
+ return (false);
+
+ /* We expect at least 4 bytes of data */
+ if (note_data[1] < 4)
+ return (false);
+
+ /* TODO: Only guard if HWCAP2_BTI is set */
+ if ((note_data[2] & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) != 0) {
+ struct arm64_guard_page_args guard;
+
+ guard.addr = (uintptr_t)obj->mapbase;
+ guard.len = obj->mapsize;
+
+ sysarch(ARM64_GUARD_PAGE, &guard);
+ }
+
+ return (true);
+}
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+
+ if (obj->pltgot != NULL) {
+ obj->pltgot[1] = (Elf_Addr) obj;
+ obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
+ }
+}
+
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Obj_Entry *srcobj, *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *srcsym;
+ const Elf_Sym *dstsym;
+ const void *srcaddr;
+ const char *name;
+ void *dstaddr;
+ SymLook req;
+ size_t size;
+ int res;
+
+ /*
+ * COPY relocs are invalid outside of the main program
+ */
+ assert(dstobj->mainprog);
+
+ relalim = (const Elf_Rela *)((const char *)dstobj->rela +
+ dstobj->relasize);
+ for (rela = dstobj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) != R_AARCH64_COPY)
+ continue;
+
+ dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = globallist_next(dstobj); srcobj != NULL;
+ srcobj = globallist_next(srcobj)) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+ if (srcobj == NULL) {
+ _rtld_error("Undefined symbol \"%s\" referenced from "
+ "COPY relocation in %s", name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase + srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+
+ return (0);
+}
+
+struct tls_data {
+ Elf_Addr dtv_gen;
+ int tls_index;
+ Elf_Addr tls_offs;
+};
+
+static struct tls_data *
+reloc_tlsdesc_alloc(int tlsindex, Elf_Addr tlsoffs)
+{
+ struct tls_data *tlsdesc;
+
+ tlsdesc = xmalloc(sizeof(struct tls_data));
+ tlsdesc->dtv_gen = tls_dtv_generation;
+ tlsdesc->tls_index = tlsindex;
+ tlsdesc->tls_offs = tlsoffs;
+
+ return (tlsdesc);
+}
+
+struct tlsdesc_entry {
+ void *(*func)(void *);
+ union {
+ Elf_Ssize addend;
+ Elf_Size offset;
+ struct tls_data *data;
+ };
+};
+
+static void
+reloc_tlsdesc(const Obj_Entry *obj, const Elf_Rela *rela,
+ struct tlsdesc_entry *where, int flags, RtldLockState *lockstate)
+{
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr offs;
+
+ offs = 0;
+ if (ELF_R_SYM(rela->r_info) != 0) {
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags,
+ NULL, lockstate);
+ if (def == NULL)
+ rtld_die();
+ offs = def->st_value;
+ obj = defobj;
+ if (def->st_shndx == SHN_UNDEF) {
+ /* Weak undefined thread variable */
+ where->func = _rtld_tlsdesc_undef;
+ where->addend = rela->r_addend;
+ return;
+ }
+ }
+ offs += rela->r_addend;
+
+ if (obj->tlsoffset != 0) {
+ /* Variable is in initialy allocated TLS segment */
+ where->func = _rtld_tlsdesc_static;
+ where->offset = obj->tlsoffset + offs;
+ } else {
+ /* TLS offest is unknown at load time, use dynamic resolving */
+ where->func = _rtld_tlsdesc_dynamic;
+ where->data = reloc_tlsdesc_alloc(obj->tlsindex, offs);
+ }
+}
+
+/*
+ * Process the PLT relocations.
+ */
+int
+reloc_plt(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def, *sym;
+ bool lazy;
+
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where, target;
+
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+
+ switch(ELF_R_TYPE(rela->r_info)) {
+ case R_AARCH64_JUMP_SLOT:
+ lazy = true;
+ if (obj->variant_pcs) {
+ sym = &obj->symtab[ELF_R_SYM(rela->r_info)];
+ /*
+ * Variant PCS functions don't follow the
+ * standard register convention. Because of
+ * this we can't use lazy relocation and
+ * need to set the target address.
+ */
+ if ((sym->st_other & STO_AARCH64_VARIANT_PCS) !=
+ 0)
+ lazy = false;
+ }
+ if (lazy) {
+ *where += (Elf_Addr)obj->relocbase;
+ } else {
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj,
+ &defobj, SYMLOOK_IN_PLT | flags, NULL,
+ lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC){
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ target = (Elf_Addr)(defobj->relocbase +
+ def->st_value);
+ /*
+ * Ignore ld_bind_not as it requires lazy
+ * binding
+ */
+ *where = target;
+ }
+ break;
+ case R_AARCH64_TLSDESC:
+ reloc_tlsdesc(obj, rela, (struct tlsdesc_entry *)where,
+ SYMLOOK_IN_PLT | flags, lockstate);
+ break;
+ case R_AARCH64_IRELATIVE:
+ obj->irelative = true;
+ break;
+ case R_AARCH64_NONE:
+ break;
+ default:
+ _rtld_error("Unknown relocation type %u in PLT",
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * LD_BIND_NOW was set - force relocation for all jump slots
+ */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+
+ if (obj->jmpslots_done)
+ return (0);
+
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where, target;
+
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ switch(ELF_R_TYPE(rela->r_info)) {
+ case R_AARCH64_JUMP_SLOT:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj,
+ &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *)rela);
+ break;
+ }
+ }
+ obj->jmpslots_done = true;
+
+ return (0);
+}
+
+static void
+reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela,
+ RtldLockState *lockstate)
+{
+ Elf_Addr *where, target, *ptr;
+
+ ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ lock_release(rtld_bind_lock, lockstate);
+ target = call_ifunc_resolver(ptr);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ *where = target;
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->irelative)
+ return (0);
+ obj->irelative = false;
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_AARCH64_IRELATIVE)
+ reloc_iresolve_one(obj, rela, lockstate);
+ }
+ return (0);
+}
+
+int
+reloc_iresolve_nonplt(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->irelative_nonplt)
+ return (0);
+ obj->irelative_nonplt = false;
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_AARCH64_IRELATIVE)
+ reloc_iresolve_one(obj, rela, lockstate);
+ }
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags,
+ struct Struct_RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_AARCH64_JUMP_SLOT) {
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+ lock_release(rtld_bind_lock, lockstate);
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *)rela);
+ }
+ }
+ obj->gnu_ifunc = false;
+ return (0);
+}
+
+Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const Obj_Entry *defobj __unused, const Obj_Entry *obj __unused,
+ const Elf_Rel *rel)
+{
+
+ assert(ELF_R_TYPE(rel->r_info) == R_AARCH64_JUMP_SLOT ||
+ ELF_R_TYPE(rel->r_info) == R_AARCH64_IRELATIVE);
+
+ if (*where != target && !ld_bind_not)
+ *where = target;
+ return (target);
+}
+
+void
+ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)
+{
+
+}
+
+/*
+ * Process non-PLT relocations
+ */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+ SymCache *cache;
+ Elf_Addr *where, symval;
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj == obj_rtld)
+ cache = NULL;
+ else
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ /*
+ * First, resolve symbol for relocations which
+ * reference symbols.
+ */
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_AARCH64_ABS64:
+ case R_AARCH64_GLOB_DAT:
+ case R_AARCH64_TLS_TPREL64:
+ case R_AARCH64_TLS_DTPREL64:
+ case R_AARCH64_TLS_DTPMOD64:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj,
+ &defobj, flags, cache, lockstate);
+ if (def == NULL)
+ return (-1);
+ /*
+ * If symbol is IFUNC, only perform relocation
+ * when caller allowed it by passing
+ * SYMLOOK_IFUNC flag. Skip the relocations
+ * otherwise.
+ *
+ * Also error out in case IFUNC relocations
+ * are specified for TLS, which cannot be
+ * usefully interpreted.
+ */
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_AARCH64_ABS64:
+ case R_AARCH64_GLOB_DAT:
+ if ((flags & SYMLOOK_IFUNC) == 0) {
+ obj->non_plt_gnu_ifunc = true;
+ continue;
+ }
+ symval = (Elf_Addr)rtld_resolve_ifunc(
+ defobj, def);
+ break;
+ default:
+ _rtld_error("%s: IFUNC for TLS reloc",
+ obj->path);
+ return (-1);
+ }
+ } else {
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ symval = (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ }
+ break;
+ default:
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ }
+
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_AARCH64_ABS64:
+ case R_AARCH64_GLOB_DAT:
+ *where = symval + rela->r_addend;
+ break;
+ case R_AARCH64_COPY:
+ /*
+ * These are deferred until all other relocations have
+ * been done. All we do here is make sure that the
+ * COPY relocation is not in a shared library. They
+ * are allowed only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_AARCH64_COPY "
+ "relocation in shared library", obj->path);
+ return (-1);
+ }
+ break;
+ case R_AARCH64_TLSDESC:
+ reloc_tlsdesc(obj, rela, (struct tlsdesc_entry *)where,
+ flags, lockstate);
+ break;
+ case R_AARCH64_TLS_TPREL64:
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_static) {
+ if (!allocate_tls_offset(
+ __DECONST(Obj_Entry *, defobj))) {
+ _rtld_error(
+ "%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ return (-1);
+ }
+ }
+ *where = def->st_value + rela->r_addend +
+ defobj->tlsoffset;
+ break;
+
+ /*
+ * !!! BEWARE !!!
+ * ARM ELF ABI defines TLS_DTPMOD64 as 1029, and TLS_DTPREL64
+ * as 1028. But actual bfd linker and the glibc RTLD linker
+ * treats TLS_DTPMOD64 as 1028 and TLS_DTPREL64 1029.
+ */
+ case R_AARCH64_TLS_DTPREL64: /* efectively is TLS_DTPMOD64 */
+ *where += (Elf_Addr)defobj->tlsindex;
+ break;
+ case R_AARCH64_TLS_DTPMOD64: /* efectively is TLS_DTPREL64 */
+ *where += (Elf_Addr)(def->st_value + rela->r_addend);
+ break;
+ case R_AARCH64_RELATIVE:
+ *where = (Elf_Addr)(obj->relocbase + rela->r_addend);
+ break;
+ case R_AARCH64_NONE:
+ break;
+ case R_AARCH64_IRELATIVE:
+ obj->irelative_nonplt = true;
+ break;
+ default:
+ rtld_printf("%s: Unhandled relocation %lu\n",
+ obj->path, ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + tls_last_size +
+ ld_static_tls_extra;
+
+ _tcb_set(allocate_tls(objs, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN));
+}
+
+void *
+__tls_get_addr(tls_index* ti)
+{
+ return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset));
+}
diff --git a/libexec/rtld-elf/aarch64/rtld_machdep.h b/libexec/rtld-elf/aarch64/rtld_machdep.h
new file mode 100644
index 000000000000..3cc1339fcad4
--- /dev/null
+++ b/libexec/rtld-elf/aarch64/rtld_machdep.h
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * Copyright (c) 2014 the FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Andrew Turner
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+#include <machine/tls.h>
+
+struct Struct_Obj_Entry;
+
+#define MD_OBJ_ENTRY \
+ bool variant_pcs : 1; /* Object has a variant pcs function */
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) \
+({ \
+ Elf_Addr _dynamic_addr; \
+ asm volatile("adr %0, _DYNAMIC" : "=&r"(_dynamic_addr)); \
+ (const Elf_Dyn *)_dynamic_addr; \
+})
+
+bool arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp);
+
+bool arch_digest_note(struct Struct_Obj_Entry *obj, const Elf_Note *note);
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj,
+ const Elf_Rel *rel);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+/*
+ * Pass zeros into the ifunc resolver so we can change them later. The first
+ * 8 arguments on arm64 are passed in registers so make them known values
+ * if we decide to use them later. Because of this ifunc resolvers can assume
+ * no arguments are passed in, and if this changes later will be able to
+ * compare the argument with 0 to see if it is set.
+ */
+#define call_ifunc_resolver(ptr) \
+ (((Elf_Addr (*)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, \
+ uint64_t, uint64_t, uint64_t))ptr)(0, 0, 0, 0, 0, 0, 0, 0))
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align, offset) \
+ round(16, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align, offset) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_post_size(align) \
+ round(TLS_TCB_SIZE, align) - TLS_TCB_SIZE
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index *ti);
+
+#define md_abi_variant_hook(x)
+
+#endif
diff --git a/libexec/rtld-elf/aarch64/rtld_start.S b/libexec/rtld-elf/aarch64/rtld_start.S
new file mode 100644
index 000000000000..fdc493198676
--- /dev/null
+++ b/libexec/rtld-elf/aarch64/rtld_start.S
@@ -0,0 +1,254 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ *
+ * This software was developed by Andrew Turner under
+ * sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include <sys/elf_common.h>
+
+ENTRY(.rtld_start)
+ .cfi_undefined x30
+ mov x19, x0 /* Put ps_strings in a callee-saved register */
+
+ sub sp, sp, #16 /* Make room for obj_main & exit proc */
+ .cfi_adjust_cfa_offset 16
+
+ mov x1, sp /* exit_proc */
+ add x2, x1, #8 /* obj_main */
+ bl _rtld /* Call the loader */
+ mov x8, x0 /* Backup the entry point */
+ ldp x2, x1, [sp], #16 /* Load cleanup, obj_main */
+ .cfi_adjust_cfa_offset 0
+
+ mov x0, x19 /* Restore ps_strings */
+ br x8 /* Jump to the entry point */
+END(.rtld_start)
+
+/*
+ * sp + 0 = &GOT[x + 3]
+ * sp + 8 = RA
+ * x16 = &GOT[2]
+ * x17 = &_rtld_bind_start
+ */
+ENTRY(_rtld_bind_start)
+ mov x17, sp
+
+ /* Save frame pointer and SP */
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ .cfi_def_cfa x29, 16
+ .cfi_offset x30, -8
+ .cfi_offset x29, -16
+
+ /* Save the arguments */
+ stp x0, x1, [sp, #-16]!
+ stp x2, x3, [sp, #-16]!
+ stp x4, x5, [sp, #-16]!
+ stp x6, x7, [sp, #-16]!
+ stp x8, xzr, [sp, #-16]!
+
+ /* Save any floating-point arguments */
+ stp q0, q1, [sp, #-32]!
+ stp q2, q3, [sp, #-32]!
+ stp q4, q5, [sp, #-32]!
+ stp q6, q7, [sp, #-32]!
+
+ /* Calculate reloff */
+ ldr x2, [x17, #0] /* Get the address of the entry */
+ sub x1, x2, x16 /* Find its offset */
+ sub x1, x1, #8 /* Adjust for x16 not being at offset 0 */
+ /* Each rela item has 3 entriesso we need reloff = 3 * index */
+ lsl x3, x1, #1 /* x3 = 2 * offset */
+ add x1, x1, x3 /* x1 = x3 + offset = 3 * offset */
+
+ /* Load obj */
+ ldr x0, [x16, #-8]
+
+ /* Call into rtld */
+ bl _rtld_bind
+
+ /* Backup the address to branch to */
+ mov x16, x0
+
+ /* restore the arguments */
+ ldp q6, q7, [sp], #32
+ ldp q4, q5, [sp], #32
+ ldp q2, q3, [sp], #32
+ ldp q0, q1, [sp], #32
+ ldp x8, xzr, [sp], #16
+ ldp x6, x7, [sp], #16
+ ldp x4, x5, [sp], #16
+ ldp x2, x3, [sp], #16
+ ldp x0, x1, [sp], #16
+
+ /* Restore frame pointer */
+ ldp x29, xzr, [sp], #16
+
+ /* Restore link register saved by the plt code */
+ ldp xzr, x30, [sp], #16
+
+ /* Call into the correct function */
+ br x16
+END(_rtld_bind_start)
+
+/*
+ * struct rel_tlsdesc {
+ * uint64_t resolver_fnc;
+ * uint64_t resolver_arg;
+ *
+ *
+ * uint64_t _rtld_tlsdesc_static(struct rel_tlsdesc *);
+ *
+ * Resolver function for TLS symbols resolved at load time
+ */
+ENTRY(_rtld_tlsdesc_static)
+ ldr x0, [x0, #8]
+ ret
+END(_rtld_tlsdesc_static)
+
+/*
+ * uint64_t _rtld_tlsdesc_undef(void);
+ *
+ * Resolver function for weak and undefined TLS symbols
+ */
+ENTRY(_rtld_tlsdesc_undef)
+ str x1, [sp, #-16]!
+ .cfi_adjust_cfa_offset 16
+
+ mrs x1, tpidr_el0
+ ldr x0, [x0, #8]
+ sub x0, x0, x1
+
+ ldr x1, [sp], #16
+ .cfi_adjust_cfa_offset -16
+ ret
+END(_rtld_tlsdesc_undef)
+
+/*
+ * uint64_t _rtld_tlsdesc_dynamic(struct rel_tlsdesc *);
+ *
+ * Resolver function for TLS symbols from dlopen()
+ */
+ENTRY(_rtld_tlsdesc_dynamic)
+ /* Save registers used in fast path */
+ stp x1, x2, [sp, #(-2 * 16)]!
+ stp x3, x4, [sp, #(1 * 16)]
+ .cfi_adjust_cfa_offset 2 * 16
+ .cfi_rel_offset x1, 0
+ .cfi_rel_offset x2, 8
+ .cfi_rel_offset x3, 16
+ .cfi_rel_offset x4, 24
+
+ /* Test fastpath - inlined version of tls_get_addr_common(). */
+ ldr x1, [x0, #8] /* tlsdesc ptr */
+ mrs x4, tpidr_el0
+ ldr x0, [x4] /* DTV pointer */
+ ldr x2, [x0] /* dtv[0] (generation count) */
+ ldr x3, [x1] /* tlsdec->dtv_gen */
+ cmp x2, x3
+ b.ne 1f /* dtv[0] != tlsdec->dtv_gen */
+
+ ldr w2, [x1, #8] /* tlsdec->tls_index */
+ add w2, w2, #1
+ ldr x3, [x0, w2, sxtw #3] /* dtv[tlsdesc->tls_index + 1] */
+ cbz x3, 1f
+
+ /* Return (dtv[tlsdesc->tls_index + 1] + tlsdesc->tls_offs - tp) */
+ ldr x2, [x1, #16] /* tlsdec->tls_offs */
+ add x2, x2, x3
+ sub x0, x2, x4
+ /* Restore registers and return */
+ ldp x3, x4, [sp, #(1 * 16)]
+ ldp x1, x2, [sp], #(2 * 16)
+ .cfi_adjust_cfa_offset -2 * 16
+ ret
+
+ /*
+ * Slow path
+ * return(
+ * tls_get_addr_common(tcb, tlsdesc->tls_index, tlsdesc->tls_offs));
+ *
+ */
+1:
+ /* Save all integer registers */
+ stp x29, x30, [sp, #-(8 * 16)]!
+ .cfi_adjust_cfa_offset 8 * 16
+ .cfi_rel_offset x29, 0
+ .cfi_rel_offset x30, 8
+
+ mov x29, sp
+ stp x5, x6, [sp, #(1 * 16)]
+ stp x7, x8, [sp, #(2 * 16)]
+ stp x9, x10, [sp, #(3 * 16)]
+ stp x11, x12, [sp, #(4 * 16)]
+ stp x13, x14, [sp, #(5 * 16)]
+ stp x15, x16, [sp, #(6 * 16)]
+ stp x17, x18, [sp, #(7 * 16)]
+ .cfi_rel_offset x5, 16
+ .cfi_rel_offset x6, 24
+ .cfi_rel_offset x7, 32
+ .cfi_rel_offset x8, 40
+ .cfi_rel_offset x9, 48
+ .cfi_rel_offset x10, 56
+ .cfi_rel_offset x11, 64
+ .cfi_rel_offset x12, 72
+ .cfi_rel_offset x13, 80
+ .cfi_rel_offset x14, 88
+ .cfi_rel_offset x15, 96
+ .cfi_rel_offset x16, 104
+ .cfi_rel_offset x17, 112
+ .cfi_rel_offset x18, 120
+
+ /* Find the tls offset */
+ mov x0, x4 /* tcb */
+ mov x3, x1 /* tlsdesc ptr */
+ ldr w1, [x3, #8] /* tlsdec->tls_index */
+ ldr x2, [x3, #16] /* tlsdec->tls_offs */
+ bl tls_get_addr_common
+ mrs x1, tpidr_el0
+ sub x0, x0, x1
+
+ /* Restore slow patch registers */
+ ldp x17, x18, [sp, #(7 * 16)]
+ ldp x15, x16, [sp, #(6 * 16)]
+ ldp x13, x14, [sp, #(5 * 16)]
+ ldp x11, x12, [sp, #(4 * 16)]
+ ldp x9, x10, [sp, #(3 * 16)]
+ ldp x7, x8, [sp, #(2 * 16)]
+ ldp x5, x6, [sp, #(1 * 16)]
+ ldp x29, x30, [sp], #(8 * 16)
+ .cfi_adjust_cfa_offset -8 * 16
+ .cfi_restore x29
+ .cfi_restore x30
+
+ /* Restore fast path registers and return */
+ ldp x3, x4, [sp, #16]
+ ldp x1, x2, [sp], #(2 * 16)
+ .cfi_adjust_cfa_offset -2 * 16
+ ret
+END(_rtld_tlsdesc_dynamic)
+
+GNU_PROPERTY_AARCH64_FEATURE_1_NOTE(GNU_PROPERTY_AARCH64_FEATURE_1_VAL)
diff --git a/libexec/rtld-elf/amd64/Makefile.inc b/libexec/rtld-elf/amd64/Makefile.inc
new file mode 100644
index 000000000000..c1036df29a0e
--- /dev/null
+++ b/libexec/rtld-elf/amd64/Makefile.inc
@@ -0,0 +1 @@
+CFLAGS+= ${CFLAGS_NO_SIMD} -msoft-float -fvisibility=hidden
diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c
new file mode 100644
index 000000000000..b1a2069edb2f
--- /dev/null
+++ b/libexec/rtld-elf/amd64/reloc.c
@@ -0,0 +1,582 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 1996, 1997, 1998, 1999 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Dynamic linker for ELF.
+ *
+ * John Polstra <jdp@polstra.com>.
+ */
+
+#define _WANT_P_OSREL
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+#include <machine/sysarch.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "rtld_tls.h"
+
+/*
+ * Process the special R_X86_64_COPY relocations in the main program. These
+ * copy data from a shared object into a region in the main program's BSS
+ * segment.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
+
+ relalim = (const Elf_Rela *)((const char *)dstobj->rela +
+ dstobj->relasize);
+ for (rela = dstobj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_X86_64_COPY) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj,
+ ELF_R_SYM(rela->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = globallist_next(dstobj); srcobj != NULL;
+ srcobj = globallist_next(srcobj)) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+
+ if (srcobj == NULL) {
+ _rtld_error(
+ "Undefined symbol \"%s\" referenced from COPY relocation in %s",
+ name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase +
+ srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+ }
+
+ return (0);
+}
+
+/* Initialize the special GOT entries. */
+void
+init_pltgot(Obj_Entry *obj)
+{
+ if (obj->pltgot != NULL) {
+ obj->pltgot[1] = (Elf_Addr)obj;
+ obj->pltgot[2] = (Elf_Addr)&_rtld_bind_start;
+ }
+}
+
+/* Process the non-PLT relocations. */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ SymCache *cache;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr *where, symval;
+ Elf32_Addr *where32;
+ int r;
+
+ r = -1;
+ symval = 0;
+ def = NULL;
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj != obj_rtld) {
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+ } else
+ cache = NULL;
+
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ /*
+ * First, resolve symbol for relocations which
+ * reference symbols.
+ */
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_64:
+ case R_X86_64_PC32:
+ case R_X86_64_GLOB_DAT:
+ case R_X86_64_TPOFF64:
+ case R_X86_64_TPOFF32:
+ case R_X86_64_DTPMOD64:
+ case R_X86_64_DTPOFF64:
+ case R_X86_64_DTPOFF32:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+ if (def == NULL)
+ goto done;
+
+ /*
+ * If symbol is IFUNC, only perform relocation
+ * when caller allowed it by passing
+ * SYMLOOK_IFUNC flag. Skip the relocations
+ * otherwise.
+ *
+ * Also error out in case IFUNC relocations
+ * are specified for TLS, which cannot be
+ * usefully interpreted.
+ */
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_64:
+ case R_X86_64_PC32:
+ case R_X86_64_GLOB_DAT:
+ if ((flags & SYMLOOK_IFUNC) == 0) {
+ obj->non_plt_gnu_ifunc = true;
+ continue;
+ }
+ symval = (Elf_Addr)rtld_resolve_ifunc(
+ defobj, def);
+ break;
+ case R_X86_64_TPOFF64:
+ case R_X86_64_TPOFF32:
+ case R_X86_64_DTPMOD64:
+ case R_X86_64_DTPOFF64:
+ case R_X86_64_DTPOFF32:
+ _rtld_error("%s: IFUNC for TLS reloc",
+ obj->path);
+ goto done;
+ }
+ } else {
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ symval = (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ }
+ break;
+ default:
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ break;
+ }
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ where32 = (Elf32_Addr *)where;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_NONE:
+ break;
+ case R_X86_64_64:
+ *where = symval + rela->r_addend;
+ break;
+ case R_X86_64_PC32:
+ /*
+ * I don't think the dynamic linker should
+ * ever see this type of relocation. But the
+ * binutils-2.6 tools sometimes generate it.
+ */
+ *where32 = (Elf32_Addr)(unsigned long)(symval +
+ rela->r_addend - (Elf_Addr)where);
+ break;
+ /* missing: R_X86_64_GOT32 R_X86_64_PLT32 */
+ case R_X86_64_COPY:
+ /*
+ * These are deferred until all other
+ * relocations have been done. All we do here
+ * is make sure that the COPY relocation is
+ * not in a shared library. They are allowed
+ * only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error(
+ "%s: Unexpected R_X86_64_COPY relocation in shared library",
+ obj->path);
+ goto done;
+ }
+ break;
+ case R_X86_64_GLOB_DAT:
+ *where = symval;
+ break;
+ case R_X86_64_TPOFF64:
+ /*
+ * We lazily allocate offsets for static TLS
+ * as we see the first relocation that
+ * references the TLS block. This allows us to
+ * support (small amounts of) static TLS in
+ * dynamically loaded modules. If we run out
+ * of space, we generate an error.
+ */
+ if (!defobj->tls_static) {
+ if (!allocate_tls_offset(__DECONST(Obj_Entry *,
+ defobj))) {
+ _rtld_error(
+ "%s: No space available for static Thread Local Storage",
+ obj->path);
+ goto done;
+ }
+ }
+ *where = (Elf_Addr)(def->st_value - defobj->tlsoffset +
+ rela->r_addend);
+ break;
+ case R_X86_64_TPOFF32:
+ /*
+ * We lazily allocate offsets for static TLS
+ * as we see the first relocation that
+ * references the TLS block. This allows us to
+ * support (small amounts of) static TLS in
+ * dynamically loaded modules. If we run out
+ * of space, we generate an error.
+ */
+ if (!defobj->tls_static) {
+ if (!allocate_tls_offset(__DECONST(Obj_Entry *,
+ defobj))) {
+ _rtld_error(
+ "%s: No space available for static Thread Local Storage",
+ obj->path);
+ goto done;
+ }
+ }
+ *where32 = (Elf32_Addr)(def->st_value -
+ defobj->tlsoffset + rela->r_addend);
+ break;
+ case R_X86_64_DTPMOD64:
+ *where += (Elf_Addr)defobj->tlsindex;
+ break;
+ case R_X86_64_DTPOFF64:
+ *where += (Elf_Addr)(def->st_value + rela->r_addend);
+ break;
+ case R_X86_64_DTPOFF32:
+ *where32 += (Elf32_Addr)(def->st_value +
+ rela->r_addend);
+ break;
+ case R_X86_64_RELATIVE:
+ *where = (Elf_Addr)(obj->relocbase + rela->r_addend);
+ break;
+ case R_X86_64_IRELATIVE:
+ obj->irelative_nonplt = true;
+ break;
+
+ /*
+ * missing:
+ * R_X86_64_GOTPCREL, R_X86_64_32, R_X86_64_32S, R_X86_64_16,
+ * R_X86_64_PC16, R_X86_64_8, R_X86_64_PC8
+ */
+ default:
+ _rtld_error(
+ "%s: Unsupported relocation type %u in non-PLT relocations",
+ obj->path, (unsigned int)ELF_R_TYPE(rela->r_info));
+ goto done;
+ }
+ }
+ r = 0;
+done:
+ free(cache);
+ return (r);
+}
+
+/* Process the PLT relocations. */
+int
+reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ /* Relocate the GOT slot pointing into the PLT. */
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ *where += (Elf_Addr)obj->relocbase;
+ break;
+
+ case R_X86_64_IRELATIVE:
+ obj->irelative = true;
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/* Relocate the jump slots in an object. */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (obj->jmpslots_done)
+ return (0);
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj,
+ &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ target = (Elf_Addr)(defobj->relocbase + def->st_value +
+ rela->r_addend);
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *)rela);
+ break;
+
+ case R_X86_64_IRELATIVE:
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ }
+ obj->jmpslots_done = true;
+ return (0);
+}
+
+/* Fixup the jump slot at "where" to transfer control to "target". */
+Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *obj __unused,
+ const struct Struct_Obj_Entry *refobj __unused, const Elf_Rel *rel __unused)
+{
+ dbg("reloc_jmpslot: *%p = %p", where, (void *)target);
+ if (!ld_bind_not)
+ *where = target;
+ return (target);
+}
+
+static void
+reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela,
+ RtldLockState *lockstate)
+{
+ Elf_Addr *where, target, *ptr;
+
+ ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ lock_release(rtld_bind_lock, lockstate);
+ target = call_ifunc_resolver(ptr);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ *where = target;
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->irelative)
+ return (0);
+ obj->irelative = false;
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_X86_64_IRELATIVE)
+ reloc_iresolve_one(obj, rela, lockstate);
+ }
+ return (0);
+}
+
+int
+reloc_iresolve_nonplt(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->irelative_nonplt)
+ return (0);
+ obj->irelative_nonplt = false;
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_X86_64_IRELATIVE)
+ reloc_iresolve_one(obj, rela, lockstate);
+ }
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+ lock_release(rtld_bind_lock, lockstate);
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *)rela);
+ break;
+ }
+ }
+ obj->gnu_ifunc = false;
+ return (0);
+}
+
+uint32_t cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2;
+
+void
+ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)
+{
+ u_int p[4], cpu_high;
+
+ do_cpuid(1, p);
+ cpu_feature = p[3];
+ cpu_feature2 = p[2];
+ do_cpuid(0, p);
+ cpu_high = p[0];
+ if (cpu_high >= 7) {
+ cpuid_count(7, 0, p);
+ cpu_stdext_feature = p[1];
+ cpu_stdext_feature2 = p[2];
+ }
+}
+
+int __getosreldate(void);
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ void *addr;
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic
+ * modules to use.
+ */
+ tls_static_space = tls_last_offset + ld_static_tls_extra;
+
+ addr = allocate_tls(objs, 0, TLS_TCB_SIZE, TLS_TCB_ALIGN);
+
+ /*
+ * This does not use _tcb_set() as it calls amd64_set_tlsbase()
+ * which is an ifunc and rtld must not use ifuncs.
+ */
+ if (__getosreldate() >= P_OSREL_TLSBASE)
+ sysarch(AMD64_SET_TLSBASE, &addr);
+ else if ((cpu_stdext_feature & CPUID_STDEXT_FSGSBASE) != 0)
+ wrfsbase((uintptr_t)addr);
+ else
+ sysarch(AMD64_SET_FSBASE, &addr);
+}
+
+void *
+__tls_get_addr(tls_index *ti)
+{
+ return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset));
+}
+
+size_t
+calculate_tls_offset(size_t prev_offset, size_t prev_size __unused, size_t size,
+ size_t align, size_t offset)
+{
+ size_t res;
+
+ /*
+ * res is the smallest integer satisfying res - prev_offset >= size
+ * and (-res) % p_align = p_vaddr % p_align (= p_offset % p_align).
+ */
+ res = prev_offset + size + align - 1;
+ res -= (res + offset) & (align - 1);
+ return (res);
+}
+
+size_t
+calculate_first_tls_offset(size_t size, size_t align, size_t offset)
+{
+ return (calculate_tls_offset(0, 0, size, align, offset));
+}
diff --git a/libexec/rtld-elf/amd64/rtld_machdep.h b/libexec/rtld-elf/amd64/rtld_machdep.h
new file mode 100644
index 000000000000..1797d13c847d
--- /dev/null
+++ b/libexec/rtld-elf/amd64/rtld_machdep.h
@@ -0,0 +1,83 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+#include <machine/tls.h>
+
+struct Struct_Obj_Entry;
+
+#define MD_OBJ_ENTRY
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+Elf_Dyn *rtld_dynamic_addr(void);
+#define rtld_dynamic(obj) rtld_dynamic_addr()
+
+/* No arch-specific dynamic tags */
+#define arch_digest_dynamic(obj, dynp) false
+
+/* No architecture specific notes */
+#define arch_digest_note(obj, note) false
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj,
+ const Elf_Rel *rel);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+extern uint32_t cpu_feature;
+extern uint32_t cpu_feature2;
+extern uint32_t cpu_stdext_feature;
+extern uint32_t cpu_stdext_feature2;
+#define call_ifunc_resolver(ptr) \
+ (((Elf_Addr (*)(uint32_t, uint32_t, uint32_t, uint32_t))ptr)( \
+ cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2))
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+void *__tls_get_addr(tls_index *ti) __exported;
+
+#define md_abi_variant_hook(x)
+
+size_t calculate_first_tls_offset(size_t size, size_t align, size_t offset);
+size_t calculate_tls_offset(size_t prev_offset, size_t prev_size, size_t size,
+ size_t align, size_t offset);
+#endif
diff --git a/libexec/rtld-elf/amd64/rtld_start.S b/libexec/rtld-elf/amd64/rtld_start.S
new file mode 100644
index 000000000000..7f85a9f94d5c
--- /dev/null
+++ b/libexec/rtld-elf/amd64/rtld_start.S
@@ -0,0 +1,174 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ .text
+ .align 4
+ .globl .rtld_start
+ .type .rtld_start,@function
+.rtld_start:
+ .cfi_startproc
+ .cfi_undefined %rip
+ xorq %rbp,%rbp # Clear frame pointer for good form
+ subq $24,%rsp # A place to store exit procedure addr
+ .cfi_def_cfa_offset 32
+ movq %rdi,%r12
+ movq %rsp,%rsi # save address of exit proc
+ movq %rsp,%rdx # construct address of obj_main
+ addq $8,%rdx
+ call _rtld # Call rtld(sp); returns entry point
+ popq %rsi # Get exit procedure address
+ .cfi_def_cfa_offset 24
+ movq %r12,%rdi # *ap
+/*
+ * At this point, %rax contains the entry point of the main program, and
+ * %rdx contains a pointer to a termination function that should be
+ * registered with atexit(). (crt1.o registers it.)
+ */
+.globl .rtld_goto_main
+.rtld_goto_main: # This symbol exists just to make debugging easier.
+ jmp *%rax # Enter main program
+ .cfi_endproc
+
+
+/*
+ * Binder entry point. Control is transferred to here by code in the PLT.
+ * On entry, there are two arguments on the stack. In ascending address
+ * order, they are (1) "obj", a pointer to the calling object's Obj_Entry,
+ * and (2) "reloff", the byte offset of the appropriate relocation entry
+ * in the PLT relocation table.
+ *
+ * We are careful to preserve all registers, even the caller-save
+ * registers. That is because this code may be invoked by low-level
+ * assembly-language code that is not ABI-compliant.
+ *
+ * Stack map:
+ * reloff 0x60
+ * obj 0x58
+ * spare 0x50
+ * rflags 0x48
+ * rax 0x40
+ * rdx 0x38
+ * rcx 0x30
+ * rsi 0x28
+ * rdi 0x20
+ * r8 0x18
+ * r9 0x10
+ * r10 0x8
+ * r11 0x0
+ */
+ .align 4
+ .globl _rtld_bind_start
+ .type _rtld_bind_start,@function
+_rtld_bind_start:
+ .cfi_startproc
+ .cfi_adjust_cfa_offset 16
+ subq $8,%rsp
+ .cfi_adjust_cfa_offset 8
+ pushfq # Save rflags
+ .cfi_adjust_cfa_offset 8
+ pushq %rax # Save %rax
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rax,-32
+ pushq %rdx # Save %rdx
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rdx,-40
+ pushq %rcx # Save %rcx
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rcx,-48
+ pushq %rsi # Save %rsi
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rsi,-56
+ pushq %rdi # Save %rdi
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rdi,-64
+ pushq %r8 # Save %r8
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %r8,-72
+ pushq %r9 # Save %r9
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %r9,-80
+ pushq %r10 # Save %r10
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %r10,-88
+ pushq %r11 # Save %r11
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %r11,-96
+
+ movq 0x58(%rsp),%rdi # Fetch obj argument
+ movq 0x60(%rsp),%rsi # Fetch reloff argument
+ leaq (%rsi,%rsi,2),%rsi # multiply by 3
+ leaq (,%rsi,8),%rsi # now 8, for 24 (sizeof Elf_Rela)
+
+ call _rtld_bind # Transfer control to the binder
+ /* Now %rax contains the entry point of the function being called. */
+
+ movq %rax,0x60(%rsp) # Store target over reloff argument
+ popq %r11 # Restore %r11
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %r11
+ popq %r10 # Restore %r10
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %r10
+ popq %r9 # Restore %r9
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %r9
+ popq %r8 # Restore %r8
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %r8
+ popq %rdi # Restore %rdi
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rdi
+ popq %rsi # Restore %rsi
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rsi
+ popq %rcx # Restore %rcx
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rcx
+ popq %rdx # Restore %rdx
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rdx
+ popq %rax # Restore %rax
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rax
+ popfq # Restore rflags
+ .cfi_adjust_cfa_offset -8
+ leaq 16(%rsp),%rsp # Discard spare, obj, do not change rflags
+ ret # "Return" to target address
+ .cfi_endproc
+ .size _rtld_bind_start, . - _rtld_bind_start
+
+ .align 4
+ .globl rtld_dynamic_addr
+ .type rtld_dynamic_addr,@function
+rtld_dynamic_addr:
+ .cfi_startproc
+ .weak _DYNAMIC
+ .hidden _DYNAMIC
+ lea _DYNAMIC(%rip),%rax
+ ret
+ .cfi_endproc
+ .size rtld_dynamic_addr, . - rtld_dynamic_addr
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/arm/Makefile.inc b/libexec/rtld-elf/arm/Makefile.inc
new file mode 100644
index 000000000000..b92dedb0285f
--- /dev/null
+++ b/libexec/rtld-elf/arm/Makefile.inc
@@ -0,0 +1 @@
+CFLAGS+= -mfpu=none
diff --git a/libexec/rtld-elf/arm/reloc.c b/libexec/rtld-elf/arm/reloc.c
new file mode 100644
index 000000000000..d1c7d3e43349
--- /dev/null
+++ b/libexec/rtld-elf/arm/reloc.c
@@ -0,0 +1,469 @@
+/* $NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "machine/sysarch.h"
+
+#include "debug.h"
+#include "rtld.h"
+#include "rtld_paths.h"
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+ if (obj->pltgot != NULL) {
+ obj->pltgot[1] = (Elf_Addr) obj;
+ obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
+ }
+}
+
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
+
+ rellim = (const Elf_Rel *)((const char *) dstobj->rel + dstobj->relsize);
+ for (rel = dstobj->rel; rel < rellim; rel++) {
+ if (ELF_R_TYPE(rel->r_info) == R_ARM_COPY) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ dstaddr = (void *)(dstobj->relocbase + rel->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj,
+ ELF_R_SYM(rel->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = globallist_next(dstobj); srcobj != NULL;
+ srcobj = globallist_next(srcobj)) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+ if (srcobj == NULL) {
+ _rtld_error(
+"Undefined symbol \"%s\" referenced from COPY relocation in %s",
+ name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase +
+ srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+ }
+ return 0;
+}
+
+void _rtld_bind_start(void);
+void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
+
+void
+_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
+{
+ const Elf_Rel *rel = NULL, *rellim;
+ Elf_Addr relsz = 0;
+ Elf_Addr *where;
+
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_REL:
+ rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
+ break;
+ case DT_RELSZ:
+ relsz = dynp->d_un.d_val;
+ break;
+ }
+ }
+ rellim = (const Elf_Rel *)((const char *)rel + relsz);
+ for (; rel < rellim; rel++) {
+ where = (Elf_Addr *)(relocbase + rel->r_offset);
+
+ *where += (Elf_Addr)relocbase;
+ }
+}
+/*
+ * It is possible for the compiler to emit relocations for unaligned data.
+ * We handle this situation with these inlines.
+ */
+#define RELOC_ALIGNED_P(x) \
+ (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
+
+static __inline Elf_Addr
+load_ptr(void *where)
+{
+ Elf_Addr res;
+
+ memcpy(&res, where, sizeof(res));
+
+ return (res);
+}
+
+static __inline void
+store_ptr(void *where, Elf_Addr val)
+{
+
+ memcpy(where, &val, sizeof(val));
+}
+
+static int
+reloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache,
+ int flags, RtldLockState *lockstate)
+{
+ Elf_Addr *where;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr tmp;
+ unsigned long symnum;
+
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ symnum = ELF_R_SYM(rel->r_info);
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_ARM_NONE:
+ break;
+
+#if 1 /* XXX should not occur */
+ case R_ARM_PC24: { /* word32 S - P + A */
+ Elf32_Sword addend;
+
+ /*
+ * Extract addend and sign-extend if needed.
+ */
+ addend = *where;
+ if (addend & 0x00800000)
+ addend |= 0xff000000;
+
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+ tmp = (Elf_Addr)obj->relocbase + def->st_value
+ - (Elf_Addr)where + (addend << 2);
+ if ((tmp & 0xfe000000) != 0xfe000000 &&
+ (tmp & 0xfe000000) != 0) {
+ _rtld_error(
+ "%s: R_ARM_PC24 relocation @ %p to %s failed "
+ "(displacement %ld (%#lx) out of range)",
+ obj->path, where,
+ obj->strtab + obj->symtab[symnum].st_name,
+ (long) tmp, (long) tmp);
+ return -1;
+ }
+ tmp >>= 2;
+ *where = (*where & 0xff000000) | (tmp & 0x00ffffff);
+ dbg("PC24 %s in %s --> %p @ %p in %s",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)*where, where, defobj->path);
+ break;
+ }
+#endif
+
+ case R_ARM_ABS32: /* word32 B + S + A */
+ case R_ARM_GLOB_DAT: /* word32 B + S */
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+ if (__predict_true(RELOC_ALIGNED_P(where))) {
+ tmp = *where + (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ *where = tmp;
+ } else {
+ tmp = load_ptr(where) +
+ (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ store_ptr(where, tmp);
+ }
+ dbg("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)tmp, where, defobj->path);
+ break;
+
+ case R_ARM_RELATIVE: /* word32 B + A */
+ if (__predict_true(RELOC_ALIGNED_P(where))) {
+ tmp = *where + (Elf_Addr)obj->relocbase;
+ *where = tmp;
+ } else {
+ tmp = load_ptr(where) +
+ (Elf_Addr)obj->relocbase;
+ store_ptr(where, tmp);
+ }
+ dbg("RELATIVE in %s --> %p", obj->path,
+ (void *)tmp);
+ break;
+
+ case R_ARM_COPY:
+ /*
+ * These are deferred until all other relocations have
+ * been done. All we do here is make sure that the
+ * COPY relocation is not in a shared library. They
+ * are allowed only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error(
+ "%s: Unexpected R_COPY relocation in shared library",
+ obj->path);
+ return -1;
+ }
+ dbg("COPY (avoid in main)");
+ break;
+
+ case R_ARM_TLS_DTPOFF32:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+
+ tmp = (Elf_Addr)(def->st_value);
+ if (__predict_true(RELOC_ALIGNED_P(where)))
+ *where = tmp;
+ else
+ store_ptr(where, tmp);
+
+ dbg("TLS_DTPOFF32 %s in %s --> %p",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)tmp);
+
+ break;
+ case R_ARM_TLS_DTPMOD32:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+
+ tmp = (Elf_Addr)(defobj->tlsindex);
+ if (__predict_true(RELOC_ALIGNED_P(where)))
+ *where = tmp;
+ else
+ store_ptr(where, tmp);
+
+ dbg("TLS_DTPMOD32 %s in %s --> %p",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)tmp);
+
+ break;
+
+ case R_ARM_TLS_TPOFF32:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+
+ if (!defobj->tls_static && !allocate_tls_offset(obj))
+ return -1;
+
+ tmp = (Elf_Addr)def->st_value + defobj->tlsoffset;
+ if (__predict_true(RELOC_ALIGNED_P(where))) {
+ tmp += *where;
+ *where = tmp;
+ } else {
+ tmp += load_ptr(where);
+ store_ptr(where, tmp);
+ }
+ dbg("TLS_TPOFF32 %s in %s --> %p",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)tmp);
+ break;
+
+
+ default:
+ dbg("sym = %lu, type = %lu, offset = %p, "
+ "contents = %p, symbol = %s",
+ symnum, (u_long)ELF_R_TYPE(rel->r_info),
+ (void *)rel->r_offset, (void *)load_ptr(where),
+ obj->strtab + obj->symtab[symnum].st_name);
+ _rtld_error("%s: Unsupported relocation type %ld "
+ "in non-PLT relocations\n",
+ obj->path, (u_long) ELF_R_TYPE(rel->r_info));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * * Process non-PLT relocations
+ * */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ SymCache *cache;
+ int r = -1;
+
+ /* The relocation for the dynamic loader has already been done. */
+ if (obj == obj_rtld)
+ return (0);
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ /* XXX not implemented */
+ return (0);
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+
+ rellim = (const Elf_Rel *)((const char *)obj->rel + obj->relsize);
+ for (rel = obj->rel; rel < rellim; rel++) {
+ if (reloc_nonplt_object(obj, rel, cache, flags, lockstate) < 0)
+ goto done;
+ }
+ r = 0;
+done:
+ if (cache != NULL)
+ free(cache);
+ return (r);
+}
+
+/*
+ * * Process the PLT relocations.
+ * */
+int
+reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ rellim = (const Elf_Rel *)((const char *)obj->pltrel +
+ obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where;
+
+ assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
+
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ *where += (Elf_Addr )obj->relocbase;
+ }
+
+ return (0);
+}
+
+/*
+ * * LD_BIND_NOW was set - force relocation for all jump slots
+ * */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ const Elf_Sym *def;
+ Elf_Addr *where;
+ Elf_Addr target;
+
+ rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL) {
+ dbg("reloc_jmpslots: sym not found");
+ return (-1);
+ }
+
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *) rel);
+ }
+
+ obj->jmpslots_done = true;
+
+ return (0);
+}
+
+int
+reloc_iresolve(Obj_Entry *obj __unused,
+ struct Struct_RtldLockState *lockstate __unused)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_iresolve_nonplt(Obj_Entry *obj __unused,
+ struct Struct_RtldLockState *lockstate __unused)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused,
+ struct Struct_RtldLockState *lockstate __unused)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const Obj_Entry *defobj __unused, const Obj_Entry *obj __unused,
+ const Elf_Rel *rel)
+{
+
+ assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
+
+ if (*where != target && !ld_bind_not)
+ *where = target;
+ return (target);
+}
+
+void
+ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)
+{
+
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+
+ tls_static_space = tls_last_offset + tls_last_size +
+ ld_static_tls_extra;
+
+ _tcb_set(allocate_tls(objs, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN));
+}
+
+void *
+__tls_get_addr(tls_index* ti)
+{
+ return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset));
+}
diff --git a/libexec/rtld-elf/arm/rtld_machdep.h b/libexec/rtld-elf/arm/rtld_machdep.h
new file mode 100644
index 000000000000..f59b30028a3b
--- /dev/null
+++ b/libexec/rtld-elf/arm/rtld_machdep.h
@@ -0,0 +1,84 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+#include <machine/acle-compat.h>
+#include <machine/tls.h>
+
+struct Struct_Obj_Entry;
+
+#define MD_OBJ_ENTRY
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) (&_DYNAMIC)
+
+/* No arch-specific dynamic tags */
+#define arch_digest_dynamic(obj, dynp) false
+
+/* No architecture specific notes */
+#define arch_digest_note(obj, note) false
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj,
+ const Elf_Rel *rel);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+#define call_ifunc_resolver(ptr) \
+ (((Elf_Addr (*)(void))ptr)())
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align, offset) \
+ round(8, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align, offset) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_post_size(align) \
+ round(TLS_TCB_SIZE, align) - TLS_TCB_SIZE
+
+extern void *__tls_get_addr(tls_index *ti);
+
+#define md_abi_variant_hook(x)
+
+#endif
diff --git a/libexec/rtld-elf/arm/rtld_start.S b/libexec/rtld-elf/arm/rtld_start.S
new file mode 100644
index 000000000000..609a7d0603ed
--- /dev/null
+++ b/libexec/rtld-elf/arm/rtld_start.S
@@ -0,0 +1,98 @@
+/* $NetBSD: rtld_start.S,v 1.7 2002/09/12 17:18:38 mycroft Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas and by Charles M. Hannum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+ .text
+ .align 0
+ .globl .rtld_start
+ .type .rtld_start,%function
+.rtld_start:
+ mov r6, sp /* save the stack pointer */
+ bic sp, sp, #7 /* align the stack pointer */
+ sub sp, sp, #8 /* make room for obj_main & exit proc */
+ mov r4, r0 /* save ps_strings */
+ ldr sl, .L2
+ ldr r5, .L2+4
+ ldr r0, .L2+8
+.L1:
+ add sl, pc, sl
+ ldr r5, [sl, r5]
+ ldr r0, [sl, r0]
+
+ sub r1, sl, r5 /* relocbase */
+ add r0, r1, r0 /* &_DYNAMIC */
+ bl _rtld_relocate_nonplt_self
+ mov r1, sp
+ add r2, sp, #4
+ mov r0, r6 /* load the sp the kernel gave us */
+ bl _rtld /* call the shared loader */
+ mov r3, r0 /* save entry point */
+
+ ldr r2, [sp, #0] /* r2 = cleanup */
+ ldr r1, [sp, #4] /* r1 = obj_main */
+ mov sp, r6 /* restore stack */
+ mov r0, r4 /* restore ps_strings */
+ mov pc, r3 /* jump to the entry point */
+.L2:
+ .word _GLOBAL_OFFSET_TABLE_ - (.L1+8)
+ .word _GLOBAL_OFFSET_TABLE_(GOT)
+ .word _DYNAMIC(GOT)
+
+ .align 0
+ .globl _rtld_bind_start
+ .type _rtld_bind_start,%function
+/*
+ * stack[0] = RA
+ * ip = &GOT[n+3]
+ * lr = &GOT[2]
+ */
+_rtld_bind_start:
+ stmdb sp!,{r0-r5,sl,fp}
+
+ sub r1, ip, lr /* r1 = 4 * (n + 1) */
+ sub r1, r1, #4 /* r1 = 4 * n */
+ add r1, r1, r1 /* r1 = 8 * n */
+
+ ldr r0, [lr, #-4] /* get obj ptr from GOT[1] */
+ mov r4, ip /* save GOT location */
+
+ mov r5, sp /* Save the stack pointer */
+ bic sp, sp, #7 /* Align the stack pointer */
+ bl _rtld_bind /* Call the binder */
+ mov sp, r5 /* Restore the old stack pointer */
+
+ str r0, [r4] /* save address in GOT */
+ mov ip, r0 /* save new address */
+
+ ldmia sp!,{r0-r5,sl,fp,lr} /* restore the stack */
+ mov pc, ip /* jump to the new address */
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/debug.c b/libexec/rtld-elf/debug.c
new file mode 100644
index 000000000000..36bb14f42230
--- /dev/null
+++ b/libexec/rtld-elf/debug.c
@@ -0,0 +1,144 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Support for printing debugging messages.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "rtld_printf.h"
+
+static const char rel_header[] =
+ " symbol name r_info r_offset st_value st_size address value\n"
+ " ------------------------------------------------------------------------------\n";
+static const char rel_format[] = " %-25s %6lx %08lx %08lx %7d %10p %08lx\n";
+
+int debug = 0;
+
+void
+debug_printf(const char *format, ...)
+{
+ if (debug) {
+ va_list ap;
+ va_start(ap, format);
+
+ rtld_vfdprintf(STDERR_FILENO, format, ap);
+ rtld_fdputchar(STDERR_FILENO, '\n');
+
+ va_end(ap);
+ }
+}
+
+void
+dump_relocations (Obj_Entry *obj0)
+{
+ Obj_Entry *obj;
+
+ for (obj = globallist_curr(obj0); obj != NULL;
+ obj = globallist_next(obj)) {
+ dump_obj_relocations(obj);
+ }
+}
+
+void
+dump_obj_relocations (Obj_Entry *obj)
+{
+
+ rtld_printf("Object \"%s\", relocbase %p\n", obj->path, obj->relocbase);
+
+ if (obj->relsize) {
+ rtld_printf("Non-PLT Relocations: %ld\n",
+ (obj->relsize / sizeof(Elf_Rel)));
+ dump_Elf_Rel(obj, obj->rel, obj->relsize);
+ }
+
+ if (obj->relasize) {
+ rtld_printf("Non-PLT Relocations with Addend: %ld\n",
+ (obj->relasize / sizeof(Elf_Rela)));
+ dump_Elf_Rela(obj, obj->rela, obj->relasize);
+ }
+
+ if (obj->pltrelsize) {
+ rtld_printf("PLT Relocations: %ld\n",
+ (obj->pltrelsize / sizeof(Elf_Rel)));
+ dump_Elf_Rel(obj, obj->pltrel, obj->pltrelsize);
+ }
+
+ if (obj->pltrelasize) {
+ rtld_printf("PLT Relocations with Addend: %ld\n",
+ (obj->pltrelasize / sizeof(Elf_Rela)));
+ dump_Elf_Rela(obj, obj->pltrela, obj->pltrelasize);
+ }
+}
+
+void
+dump_Elf_Rel (Obj_Entry *obj, const Elf_Rel *rel0, u_long relsize)
+{
+ const Elf_Rel *rel;
+ const Elf_Rel *rellim;
+ const Elf_Sym *sym;
+ Elf_Addr *dstaddr;
+
+ rtld_putstr(rel_header);
+ rellim = (const Elf_Rel *)((const char *)rel0 + relsize);
+ for (rel = rel0; rel < rellim; rel++) {
+ dstaddr = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ sym = obj->symtab + ELF_R_SYM(rel->r_info);
+ rtld_printf(rel_format,
+ obj->strtab + sym->st_name,
+ (u_long)rel->r_info, (u_long)rel->r_offset,
+ (u_long)sym->st_value, (int)sym->st_size,
+ dstaddr, (u_long)*dstaddr);
+ }
+ return;
+}
+
+void
+dump_Elf_Rela (Obj_Entry *obj, const Elf_Rela *rela0, u_long relasize)
+{
+ const Elf_Rela *rela;
+ const Elf_Rela *relalim;
+ const Elf_Sym *sym;
+ Elf_Addr *dstaddr;
+
+ rtld_putstr(rel_header);
+ relalim = (const Elf_Rela *)((const char *)rela0 + relasize);
+ for (rela = rela0; rela < relalim; rela++) {
+ dstaddr = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ sym = obj->symtab + ELF_R_SYM(rela->r_info);
+ rtld_printf(rel_format,
+ obj->strtab + sym->st_name,
+ (u_long)rela->r_info, (u_long)rela->r_offset,
+ (u_long)sym->st_value, (int)sym->st_size,
+ dstaddr, (u_long)*dstaddr);
+ }
+ return;
+}
diff --git a/libexec/rtld-elf/debug.h b/libexec/rtld-elf/debug.h
new file mode 100644
index 000000000000..1a019f07750a
--- /dev/null
+++ b/libexec/rtld-elf/debug.h
@@ -0,0 +1,57 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Support for printing debugging messages.
+ */
+
+#ifndef DEBUG_H
+#define DEBUG_H 1
+
+#include <sys/cdefs.h>
+
+#include <string.h>
+#include "rtld_paths.h"
+#include "rtld_printf.h"
+
+void debug_printf(const char *, ...) __printflike(1, 2);
+extern int debug;
+
+#ifndef NO_LD_DEBUG
+#define dbg(...) debug_printf(__VA_ARGS__)
+#else
+#define dbg(...) ((void) 0)
+#endif
+
+#define assert(cond) ((cond) ? (void) 0 : \
+ (msg(_BASENAME_RTLD ": assert failed: " __FILE__ ":" \
+ __XSTRING(__LINE__) "\n"), abort()))
+#define msg(s) rtld_putstr(s)
+#define trace() msg(_BASENAME_RTLD ": " __XSTRING(__LINE__) "\n")
+
+
+#endif /* DEBUG_H */
diff --git a/libexec/rtld-elf/i386/Makefile.inc b/libexec/rtld-elf/i386/Makefile.inc
new file mode 100644
index 000000000000..c1036df29a0e
--- /dev/null
+++ b/libexec/rtld-elf/i386/Makefile.inc
@@ -0,0 +1 @@
+CFLAGS+= ${CFLAGS_NO_SIMD} -msoft-float -fvisibility=hidden
diff --git a/libexec/rtld-elf/i386/Symbol.map b/libexec/rtld-elf/i386/Symbol.map
new file mode 100644
index 000000000000..c7dcd4ce5032
--- /dev/null
+++ b/libexec/rtld-elf/i386/Symbol.map
@@ -0,0 +1,6 @@
+/*
+ */
+
+FBSD_1.0 {
+ ___tls_get_addr;
+};
diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c
new file mode 100644
index 000000000000..845735deac7d
--- /dev/null
+++ b/libexec/rtld-elf/i386/reloc.c
@@ -0,0 +1,555 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 1996, 1997, 1998, 1999 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Dynamic linker for ELF.
+ *
+ * John Polstra <jdp@polstra.com>.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <machine/segments.h>
+#include <machine/sysarch.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "rtld_tls.h"
+
+/*
+ * Process the special R_386_COPY relocations in the main program. These
+ * copy data from a shared object into a region in the main program's BSS
+ * segment.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
+
+ rellim = (const Elf_Rel *)((const char *)dstobj->rel + dstobj->relsize);
+ for (rel = dstobj->rel; rel < rellim; rel++) {
+ if (ELF_R_TYPE(rel->r_info) == R_386_COPY) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ dstaddr = (void *)(dstobj->relocbase + rel->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj,
+ ELF_R_SYM(rel->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = globallist_next(dstobj); srcobj != NULL;
+ srcobj = globallist_next(srcobj)) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+
+ if (srcobj == NULL) {
+ _rtld_error(
+ "Undefined symbol \"%s\" referenced from COPY"
+ " relocation in %s",
+ name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase +
+ srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+ }
+
+ return (0);
+}
+
+/* Initialize the special GOT entries. */
+void
+init_pltgot(Obj_Entry *obj)
+{
+ if (obj->pltgot != NULL) {
+ obj->pltgot[1] = (Elf_Addr)obj;
+ obj->pltgot[2] = (Elf_Addr)&_rtld_bind_start;
+ }
+}
+
+/* Process the non-PLT relocations. */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ SymCache *cache;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr *where, symval, add;
+ int r;
+
+ r = -1;
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj != obj_rtld) {
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+ } else {
+ cache = NULL;
+ }
+
+ /* Appease some compilers. */
+ symval = 0;
+ def = NULL;
+
+ rellim = (const Elf_Rel *)((const char *)obj->rel + obj->relsize);
+ for (rel = obj->rel; rel < rellim; rel++) {
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_32:
+ case R_386_PC32:
+ case R_386_GLOB_DAT:
+ case R_386_TLS_TPOFF:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ flags, cache, lockstate);
+ if (def == NULL)
+ goto done;
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_32:
+ case R_386_PC32:
+ case R_386_GLOB_DAT:
+ if ((flags & SYMLOOK_IFUNC) == 0) {
+ obj->non_plt_gnu_ifunc = true;
+ continue;
+ }
+ symval = (Elf_Addr)
+ rtld_resolve_ifunc(defobj, def);
+ break;
+ case R_386_TLS_TPOFF:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ _rtld_error("%s: IFUNC for TLS reloc",
+ obj->path);
+ goto done;
+ }
+ } else {
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ symval = (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ }
+ break;
+ default:
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ break;
+ }
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_NONE:
+ break;
+ case R_386_32:
+ *where += symval;
+ break;
+ case R_386_PC32:
+ /*
+ * I don't think the dynamic linker should ever
+ * see this type of relocation. But the
+ * binutils-2.6 tools sometimes generate it.
+ */
+ *where += symval - (Elf_Addr)where;
+ break;
+ case R_386_COPY:
+ /*
+ * These are deferred until all other
+ * relocations have been done. All we do here
+ * is make sure that the COPY relocation is
+ * not in a shared library. They are allowed
+ * only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error(
+ "%s: Unexpected R_386_COPY relocation in shared library",
+ obj->path);
+ goto done;
+ }
+ break;
+ case R_386_GLOB_DAT:
+ *where = symval;
+ break;
+ case R_386_RELATIVE:
+ *where += (Elf_Addr)obj->relocbase;
+ break;
+ case R_386_TLS_TPOFF:
+ case R_386_TLS_TPOFF32:
+ /*
+ * We lazily allocate offsets for static TLS
+ * as we see the first relocation that
+ * references the TLS block. This allows us to
+ * support (small amounts of) static TLS in
+ * dynamically loaded modules. If we run out
+ * of space, we generate an error.
+ */
+ if (!defobj->tls_static) {
+ if (!allocate_tls_offset(
+ __DECONST(Obj_Entry *, defobj))) {
+ _rtld_error(
+ "%s: No space available for static Thread Local Storage",
+ obj->path);
+ goto done;
+ }
+ }
+ add = (Elf_Addr)(def->st_value - defobj->tlsoffset);
+ if (ELF_R_TYPE(rel->r_info) == R_386_TLS_TPOFF)
+ *where += add;
+ else
+ *where -= add;
+ break;
+ case R_386_TLS_DTPMOD32:
+ *where += (Elf_Addr)defobj->tlsindex;
+ break;
+ case R_386_TLS_DTPOFF32:
+ *where += (Elf_Addr)def->st_value;
+ break;
+ case R_386_IRELATIVE:
+ obj->irelative_nonplt = true;
+ break;
+ default:
+ _rtld_error(
+ "%s: Unsupported relocation type %d in non-PLT relocations",
+ obj->path, ELF_R_TYPE(rel->r_info));
+ goto done;
+ }
+ }
+ r = 0;
+done:
+ free(cache);
+ return (r);
+}
+
+/* Process the PLT relocations. */
+int
+reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where;
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_JMP_SLOT:
+ /* Relocate the GOT slot pointing into the PLT. */
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ *where += (Elf_Addr)obj->relocbase;
+ break;
+
+ case R_386_IRELATIVE:
+ obj->irelative = true;
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ ELF_R_TYPE(rel->r_info));
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/* Relocate the jump slots in an object. */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ if (obj->jmpslots_done)
+ return (0);
+ rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ reloc_jmpslot(where, target, defobj, obj, rel);
+ break;
+
+ case R_386_IRELATIVE:
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ ELF_R_TYPE(rel->r_info));
+ return (-1);
+ }
+ }
+
+ obj->jmpslots_done = true;
+ return (0);
+}
+
+/* Fixup the jump slot at "where" to transfer control to "target". */
+Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *obj __unused,
+ const Obj_Entry *refobj __unused, const Elf_Rel *rel __unused)
+{
+ dbg("reloc_jmpslot: *%p = %p", where, (void *)target);
+ if (!ld_bind_not)
+ *where = target;
+ return (target);
+}
+
+static void
+reloc_iresolve_one(Obj_Entry *obj, const Elf_Rel *rel, RtldLockState *lockstate)
+{
+ Elf_Addr *where, target;
+
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ lock_release(rtld_bind_lock, lockstate);
+ target = call_ifunc_resolver(obj->relocbase + *where);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ *where = target;
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ if (!obj->irelative)
+ return (0);
+ obj->irelative = false;
+ rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ if (ELF_R_TYPE(rel->r_info) == R_386_IRELATIVE)
+ reloc_iresolve_one(obj, rel, lockstate);
+ }
+ return (0);
+}
+
+int
+reloc_iresolve_nonplt(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ if (!obj->irelative_nonplt)
+ return (0);
+ obj->irelative_nonplt = false;
+ rellim = (const Elf_Rel *)((const char *)obj->rel + obj->relsize);
+ for (rel = obj->rel; rel < rellim; rel++) {
+ if (ELF_R_TYPE(rel->r_info) == R_386_IRELATIVE)
+ reloc_iresolve_one(obj, rel, lockstate);
+ }
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+ rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+ lock_release(rtld_bind_lock, lockstate);
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ reloc_jmpslot(where, target, defobj, obj, rel);
+ break;
+ }
+ }
+
+ obj->gnu_ifunc = false;
+ return (0);
+}
+
+uint32_t cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2;
+
+static void
+rtld_cpuid_count(int idx, int cnt, u_int *p)
+{
+ __asm __volatile(
+ " pushl %%ebx\n"
+ " cpuid\n"
+ " movl %%ebx,%1\n"
+ " popl %%ebx\n"
+ : "=a"(p[0]), "=r"(p[1]), "=c"(p[2]), "=d"(p[3])
+ : "0"(idx), "2"(cnt));
+}
+
+void
+ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)
+{
+ u_int p[4], cpu_high;
+ int cpuid_supported;
+
+ __asm __volatile(
+ " pushfl\n"
+ " popl %%eax\n"
+ " movl %%eax,%%ecx\n"
+ " xorl $0x200000,%%eax\n"
+ " pushl %%eax\n"
+ " popfl\n"
+ " pushfl\n"
+ " popl %%eax\n"
+ " xorl %%eax,%%ecx\n"
+ " je 1f\n"
+ " movl $1,%0\n"
+ " jmp 2f\n"
+ "1: movl $0,%0\n"
+ "2:\n"
+ : "=r"(cpuid_supported)
+ :
+ : "eax", "ecx");
+ if (!cpuid_supported)
+ return;
+
+ rtld_cpuid_count(1, 0, p);
+ cpu_feature = p[3];
+ cpu_feature2 = p[2];
+ rtld_cpuid_count(0, 0, p);
+ cpu_high = p[0];
+ if (cpu_high >= 7) {
+ rtld_cpuid_count(7, 0, p);
+ cpu_stdext_feature = p[1];
+ cpu_stdext_feature2 = p[2];
+ }
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ void *tls;
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + ld_static_tls_extra;
+ tls = allocate_tls(objs, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN);
+ _tcb_set(tls);
+}
+
+/* GNU ABI */
+__attribute__((__regparm__(1))) void *
+___tls_get_addr(tls_index *ti)
+{
+ return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset));
+}
+
+/* Sun ABI */
+void *
+__tls_get_addr(tls_index *ti)
+{
+ return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset));
+}
+
+size_t
+calculate_tls_offset(size_t prev_offset, size_t prev_size __unused, size_t size,
+ size_t align, size_t offset)
+{
+ size_t res;
+
+ /*
+ * res is the smallest integer satisfying res - prev_offset >= size
+ * and (-res) % p_align = p_vaddr % p_align (= p_offset % p_align).
+ */
+ res = prev_offset + size + align - 1;
+ res -= (res + offset) & (align - 1);
+ return (res);
+}
+
+size_t
+calculate_first_tls_offset(size_t size, size_t align, size_t offset)
+{
+ return (calculate_tls_offset(0, 0, size, align, offset));
+}
diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h
new file mode 100644
index 000000000000..581f1dfb002d
--- /dev/null
+++ b/libexec/rtld-elf/i386/rtld_machdep.h
@@ -0,0 +1,84 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+#include <machine/tls.h>
+
+struct Struct_Obj_Entry;
+
+#define MD_OBJ_ENTRY
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) \
+ ((const Elf_Dyn *)((obj)->relocbase + (Elf_Addr)&_DYNAMIC))
+
+/* No arch-specific dynamic tags */
+#define arch_digest_dynamic(obj, dynp) false
+
+/* No architecture specific notes */
+#define arch_digest_note(obj, note) false
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj,
+ const Elf_Rel *rel);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+extern uint32_t cpu_feature;
+extern uint32_t cpu_feature2;
+extern uint32_t cpu_stdext_feature;
+extern uint32_t cpu_stdext_feature2;
+#define call_ifunc_resolver(ptr) \
+ (((Elf_Addr (*)(uint32_t, uint32_t, uint32_t, uint32_t))(ptr))( \
+ cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2))
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+void *___tls_get_addr(tls_index *ti) __attribute__((__regparm__(1))) __exported;
+void *__tls_get_addr(tls_index *ti) __exported;
+
+#define md_abi_variant_hook(x)
+
+size_t calculate_first_tls_offset(size_t size, size_t align, size_t offset);
+size_t calculate_tls_offset(size_t prev_offset, size_t prev_size, size_t size,
+ size_t align, size_t offset);
+#endif
diff --git a/libexec/rtld-elf/i386/rtld_start.S b/libexec/rtld-elf/i386/rtld_start.S
new file mode 100644
index 000000000000..9dbe32d27628
--- /dev/null
+++ b/libexec/rtld-elf/i386/rtld_start.S
@@ -0,0 +1,98 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ .text
+ .align 4
+ .globl .rtld_start
+ .type .rtld_start,@function
+.rtld_start:
+ .cfi_startproc
+ .cfi_undefined %eip
+ xorl %ebp,%ebp # Clear frame pointer for good form
+ movl %esp,%esi # Save initial stack pointer
+ pushl %ebp
+ .cfi_def_cfa_offset 4
+ movl %esp,%ebp
+ .cfi_offset %ebp,-4
+ .cfi_def_cfa_register %ebp
+ andl $0xfffffff0,%esp # Align stack pointer
+ subl $16,%esp # A place to store exit procedure addr
+ movl %esp,%ebx # save address of exit proc
+ movl %esp,%ecx # construct address of obj_main
+ addl $4,%ecx
+ subl $4,%esp # Keep stack aligned
+ pushl %ecx # Pass address of obj_main
+ pushl %ebx # Pass address of exit proc
+ pushl %esi # Pass initial stack pointer to rtld
+ call _rtld # Call rtld(sp); returns entry point
+ addl $16,%esp # Remove arguments from stack
+ popl %edx # Get exit procedure address
+ movl %esi,%esp # Ignore obj_main
+/*
+ * At this point, %eax contains the entry point of the main program, and
+ * %edx contains a pointer to a termination function that should be
+ * registered with atexit(). (crt1.o registers it.)
+ */
+.globl .rtld_goto_main
+.rtld_goto_main: # This symbol exists just to make debugging easier.
+ jmp *%eax # Enter main program
+ .cfi_endproc
+
+
+/*
+ * Binder entry point. Control is transferred to here by code in the PLT.
+ * On entry, there are two arguments on the stack. In ascending address
+ * order, they are (1) "obj", a pointer to the calling object's Obj_Entry,
+ * and (2) "reloff", the byte offset of the appropriate relocation entry
+ * in the PLT relocation table.
+ *
+ * We are careful to preserve all registers, even the caller-save
+ * registers. That is because this code may be invoked by low-level
+ * assembly-language code that is not ABI-compliant.
+ */
+ .align 4
+ .globl _rtld_bind_start
+ .type _rtld_bind_start,@function
+_rtld_bind_start:
+ pushf # Save eflags
+ pushl %eax # Save %eax
+ pushl %edx # Save %edx
+ pushl %ecx # Save %ecx
+ pushl 20(%esp) # Copy reloff argument
+ pushl 20(%esp) # Copy obj argument
+
+ call _rtld_bind # Transfer control to the binder
+ /* Now %eax contains the entry point of the function being called. */
+
+ addl $8,%esp # Discard binder arguments
+ movl %eax,20(%esp) # Store target over obj argument
+ popl %ecx # Restore %ecx
+ popl %edx # Restore %edx
+ popl %eax # Restore %eax
+ popf # Restore eflags
+ leal 4(%esp),%esp # Discard reloff, do not change eflags
+ ret # "Return" to target address
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/libmap.c b/libexec/rtld-elf/libmap.c
new file mode 100644
index 000000000000..26386efcf487
--- /dev/null
+++ b/libexec/rtld-elf/libmap.c
@@ -0,0 +1,497 @@
+/*
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "libmap.h"
+#include "rtld_paths.h"
+#include "rtld_libc.h"
+
+TAILQ_HEAD(lm_list, lm);
+struct lm {
+ char *f;
+ char *t;
+ TAILQ_ENTRY(lm) lm_link;
+};
+
+static TAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head);
+struct lmp {
+ char *p;
+ enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type;
+ struct lm_list lml;
+ TAILQ_ENTRY(lmp) lmp_link;
+};
+
+static TAILQ_HEAD(lmc_list, lmc) lmc_head = TAILQ_HEAD_INITIALIZER(lmc_head);
+struct lmc {
+ char *path;
+ dev_t dev;
+ ino_t ino;
+ TAILQ_ENTRY(lmc) next;
+};
+
+static int lm_count;
+
+static void lmc_parse(char *, size_t);
+static void lmc_parse_file(const char *);
+static void lmc_parse_dir(const char *);
+static void lm_add(const char *, const char *, const char *);
+static void lm_free(struct lm_list *);
+static char *lml_find(struct lm_list *, const char *);
+static struct lm_list *lmp_find(const char *);
+static struct lm_list *lmp_init(char *);
+static const char *quickbasename(const char *);
+
+#define iseol(c) (((c) == '#') || ((c) == '\0') || \
+ ((c) == '\n') || ((c) == '\r'))
+
+/*
+ * Do not use ctype.h macros, which rely on working TLS. Rtld does
+ * not support TLS for itself.
+ */
+#define rtld_isspace(c) ((c) == ' ' || (c) == '\t')
+
+int
+lm_init(const char *libmap_override)
+{
+ char *l, *p;
+
+ dbg("lm_init(\"%s\")", libmap_override);
+ TAILQ_INIT(&lmp_head);
+
+ lmc_parse_file(ld_path_libmap_conf);
+
+ if (libmap_override != NULL) {
+ /*
+ * Do some character replacement to make $LD_LIBMAP look
+ * like a text file, then parse it.
+ */
+ l = xstrdup(libmap_override);
+ for (p = l; *p != 0; p++) {
+ switch (*p) {
+ case '=':
+ *p = ' ';
+ break;
+ case ',':
+ *p = '\n';
+ break;
+ }
+ }
+ lmc_parse(l, p - l);
+ free(l);
+ }
+
+ return (lm_count == 0);
+}
+
+static void
+lmc_parse_file(const char *path)
+{
+ struct lmc *p;
+ char *lm_map;
+ struct stat st;
+ ssize_t retval;
+ int fd, saved_errno;
+
+ TAILQ_FOREACH(p, &lmc_head, next) {
+ if (strcmp(p->path, path) == 0)
+ return;
+ }
+
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ dbg("lm_parse_file: open(\"%s\") failed, %s", path,
+ rtld_strerror(errno));
+ return;
+ }
+ if (fstat(fd, &st) == -1) {
+ dbg("lm_parse_file: fstat(\"%s\") failed, %s", path,
+ rtld_strerror(errno));
+ close(fd);
+ return;
+ }
+
+ TAILQ_FOREACH(p, &lmc_head, next) {
+ if (p->dev == st.st_dev && p->ino == st.st_ino) {
+ close(fd);
+ return;
+ }
+ }
+
+ lm_map = xmalloc(st.st_size);
+ retval = read(fd, lm_map, st.st_size);
+ saved_errno = errno;
+ close(fd);
+ if (retval != st.st_size) {
+ if (retval == -1) {
+ dbg("lm_parse_file: read(\"%s\") failed, %s", path,
+ rtld_strerror(saved_errno));
+ } else {
+ dbg("lm_parse_file: short read(\"%s\"), %zd vs %jd",
+ path, retval, (uintmax_t)st.st_size);
+ }
+ free(lm_map);
+ return;
+ }
+ p = xmalloc(sizeof(struct lmc));
+ p->path = xstrdup(path);
+ p->dev = st.st_dev;
+ p->ino = st.st_ino;
+ TAILQ_INSERT_HEAD(&lmc_head, p, next);
+ lmc_parse(lm_map, st.st_size);
+ free(lm_map);
+}
+
+static void
+lmc_parse_dir(const char *idir)
+{
+ DIR *d;
+ struct dirent *dp;
+ struct lmc *p;
+ char conffile[MAXPATHLEN];
+ char *ext;
+
+ TAILQ_FOREACH(p, &lmc_head, next) {
+ if (strcmp(p->path, idir) == 0)
+ return;
+ }
+ d = opendir(idir);
+ if (d == NULL)
+ return;
+
+ p = xmalloc(sizeof(struct lmc));
+ p->path = xstrdup(idir);
+ p->dev = NODEV;
+ p->ino = 0;
+ TAILQ_INSERT_HEAD(&lmc_head, p, next);
+
+ while ((dp = readdir(d)) != NULL) {
+ if (dp->d_ino == 0)
+ continue;
+ if (dp->d_type != DT_REG)
+ continue;
+ ext = strrchr(dp->d_name, '.');
+ if (ext == NULL)
+ continue;
+ if (strcmp(ext, ".conf") != 0)
+ continue;
+ if (strlcpy(conffile, idir, MAXPATHLEN) >= MAXPATHLEN)
+ continue; /* too long */
+ if (strlcat(conffile, "/", MAXPATHLEN) >= MAXPATHLEN)
+ continue; /* too long */
+ if (strlcat(conffile, dp->d_name, MAXPATHLEN) >= MAXPATHLEN)
+ continue; /* too long */
+ lmc_parse_file(conffile);
+ }
+ closedir(d);
+}
+
+static void
+lmc_parse(char *lm_p, size_t lm_len)
+{
+ char *cp, *f, *t, *c, *p;
+ char prog[MAXPATHLEN];
+ /* allow includedir + full length path */
+ char line[MAXPATHLEN + 13];
+ size_t cnt, i;
+
+ cnt = 0;
+ p = NULL;
+ while (cnt < lm_len) {
+ i = 0;
+ while (cnt < lm_len && lm_p[cnt] != '\n' &&
+ i < sizeof(line) - 1) {
+ line[i] = lm_p[cnt];
+ cnt++;
+ i++;
+ }
+ line[i] = '\0';
+ while (cnt < lm_len && lm_p[cnt] != '\n')
+ cnt++;
+ /* skip over nl */
+ cnt++;
+
+ cp = &line[0];
+ t = f = c = NULL;
+
+ /* Skip over leading space */
+ while (rtld_isspace(*cp))
+ cp++;
+
+ /* Found a comment or EOL */
+ if (iseol(*cp))
+ continue;
+
+ /* Found a constraint selector */
+ if (*cp == '[') {
+ cp++;
+
+ /* Skip leading space */
+ while (rtld_isspace(*cp))
+ cp++;
+
+ /* Found comment, EOL or end of selector */
+ if (iseol(*cp) || *cp == ']')
+ continue;
+
+ c = cp++;
+ /* Skip to end of word */
+ while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']')
+ cp++;
+
+ /* Skip and zero out trailing space */
+ while (rtld_isspace(*cp))
+ *cp++ = '\0';
+
+ /* Check if there is a closing brace */
+ if (*cp != ']')
+ continue;
+
+ /* Terminate string if there was no trailing space */
+ *cp++ = '\0';
+
+ /*
+ * There should be nothing except whitespace or comment
+ from this point to the end of the line.
+ */
+ while (rtld_isspace(*cp))
+ cp++;
+ if (!iseol(*cp))
+ continue;
+
+ if (strlcpy(prog, c, sizeof prog) >= sizeof prog)
+ continue;
+ p = prog;
+ continue;
+ }
+
+ /* Parse the 'from' candidate. */
+ f = cp++;
+ while (!rtld_isspace(*cp) && !iseol(*cp))
+ cp++;
+
+ /* Skip and zero out the trailing whitespace */
+ while (rtld_isspace(*cp))
+ *cp++ = '\0';
+
+ /* Found a comment or EOL */
+ if (iseol(*cp))
+ continue;
+
+ /* Parse 'to' mapping */
+ t = cp++;
+ while (!rtld_isspace(*cp) && !iseol(*cp))
+ cp++;
+
+ /* Skip and zero out the trailing whitespace */
+ while (rtld_isspace(*cp))
+ *cp++ = '\0';
+
+ /* Should be no extra tokens at this point */
+ if (!iseol(*cp))
+ continue;
+
+ *cp = '\0';
+ if (strcmp(f, "includedir") == 0)
+ lmc_parse_dir(t);
+ else if (strcmp(f, "include") == 0)
+ lmc_parse_file(t);
+ else
+ lm_add(p, f, t);
+ }
+}
+
+static void
+lm_free(struct lm_list *lml)
+{
+ struct lm *lm;
+
+ dbg("%s(%p)", __func__, lml);
+
+ while (!TAILQ_EMPTY(lml)) {
+ lm = TAILQ_FIRST(lml);
+ TAILQ_REMOVE(lml, lm, lm_link);
+ free(lm->f);
+ free(lm->t);
+ free(lm);
+ }
+}
+
+void
+lm_fini(void)
+{
+ struct lmp *lmp;
+ struct lmc *p;
+
+ dbg("%s()", __func__);
+
+ while (!TAILQ_EMPTY(&lmc_head)) {
+ p = TAILQ_FIRST(&lmc_head);
+ TAILQ_REMOVE(&lmc_head, p, next);
+ free(p->path);
+ free(p);
+ }
+
+ while (!TAILQ_EMPTY(&lmp_head)) {
+ lmp = TAILQ_FIRST(&lmp_head);
+ TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
+ free(lmp->p);
+ lm_free(&lmp->lml);
+ free(lmp);
+ }
+}
+
+static void
+lm_add(const char *p, const char *f, const char *t)
+{
+ struct lm_list *lml;
+ struct lm *lm;
+ const char *t1;
+
+ if (p == NULL)
+ p = "$DEFAULT$";
+
+ dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t);
+
+ if ((lml = lmp_find(p)) == NULL)
+ lml = lmp_init(xstrdup(p));
+
+ t1 = lml_find(lml, f);
+ if (t1 == NULL || strcmp(t1, t) != 0) {
+ lm = xmalloc(sizeof(struct lm));
+ lm->f = xstrdup(f);
+ lm->t = xstrdup(t);
+ TAILQ_INSERT_HEAD(lml, lm, lm_link);
+ lm_count++;
+ }
+}
+
+char *
+lm_find(const char *p, const char *f)
+{
+ struct lm_list *lml;
+ char *t;
+
+ dbg("%s(\"%s\", \"%s\")", __func__, p, f);
+
+ if (p != NULL && (lml = lmp_find(p)) != NULL) {
+ t = lml_find(lml, f);
+ if (t != NULL) {
+ /*
+ * Add a global mapping if we have
+ * a successful constrained match.
+ */
+ lm_add(NULL, f, t);
+ return (t);
+ }
+ }
+ lml = lmp_find("$DEFAULT$");
+ if (lml != NULL)
+ return (lml_find(lml, f));
+ return (NULL);
+}
+
+/*
+ * Given a libmap translation list and a library name, return the
+ * replacement library, or NULL.
+ */
+char *
+lm_findn(const char *p, const char *f, const size_t n)
+{
+ char pathbuf[64], *s, *t;
+
+ if (n < sizeof(pathbuf) - 1)
+ s = pathbuf;
+ else
+ s = xmalloc(n + 1);
+ memcpy(s, f, n);
+ s[n] = '\0';
+ t = lm_find(p, s);
+ if (s != pathbuf)
+ free(s);
+ return (t);
+}
+
+static char *
+lml_find(struct lm_list *lmh, const char *f)
+{
+ struct lm *lm;
+
+ dbg("%s(%p, \"%s\")", __func__, lmh, f);
+
+ TAILQ_FOREACH(lm, lmh, lm_link) {
+ if (strcmp(f, lm->f) == 0)
+ return (lm->t);
+ }
+ return (NULL);
+}
+
+/*
+ * Given an executable name, return a pointer to the translation list or
+ * NULL if no matches.
+ */
+static struct lm_list *
+lmp_find(const char *n)
+{
+ struct lmp *lmp;
+
+ dbg("%s(\"%s\")", __func__, n);
+
+ TAILQ_FOREACH(lmp, &lmp_head, lmp_link) {
+ if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) ||
+ (lmp->type == T_DIRECTORY && strncmp(n, lmp->p,
+ strlen(lmp->p)) == 0) ||
+ (lmp->type == T_BASENAME && strcmp(quickbasename(n),
+ lmp->p) == 0))
+ return (&lmp->lml);
+ }
+ return (NULL);
+}
+
+static struct lm_list *
+lmp_init(char *n)
+{
+ struct lmp *lmp;
+
+ dbg("%s(\"%s\")", __func__, n);
+
+ lmp = xmalloc(sizeof(struct lmp));
+ lmp->p = n;
+ if (n[strlen(n) - 1] == '/')
+ lmp->type = T_DIRECTORY;
+ else if (strchr(n,'/') == NULL)
+ lmp->type = T_BASENAME;
+ else
+ lmp->type = T_EXACT;
+ TAILQ_INIT(&lmp->lml);
+ TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link);
+
+ return (&lmp->lml);
+}
+
+/*
+ * libc basename is overkill. Return a pointer to the character after
+ * the last /, or the original string if there are no slashes.
+ */
+static const char *
+quickbasename(const char *path)
+{
+ const char *p;
+
+ for (p = path; *path != '\0'; path++) {
+ if (*path == '/')
+ p = path + 1;
+ }
+ return (p);
+}
diff --git a/libexec/rtld-elf/libmap.conf b/libexec/rtld-elf/libmap.conf
new file mode 100644
index 000000000000..2885f2e207ec
--- /dev/null
+++ b/libexec/rtld-elf/libmap.conf
@@ -0,0 +1 @@
+includedir /usr/local/etc/libmap.d
diff --git a/libexec/rtld-elf/libmap.h b/libexec/rtld-elf/libmap.h
new file mode 100644
index 000000000000..a785f1b6c43f
--- /dev/null
+++ b/libexec/rtld-elf/libmap.h
@@ -0,0 +1,12 @@
+/*
+ */
+
+#ifndef LIBMAP_H
+#define LIBMAP_H
+
+int lm_init(const char *);
+void lm_fini(void);
+char *lm_find(const char *, const char *);
+char *lm_findn(const char *, const char *, const size_t);
+
+#endif
diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c
new file mode 100644
index 000000000000..5e5774c0b017
--- /dev/null
+++ b/libexec/rtld-elf/map_object.c
@@ -0,0 +1,525 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+static Elf_Ehdr *get_elf_header(int, const char *, const struct stat *,
+ Elf_Phdr **phdr);
+static int convert_flags(int); /* Elf flags -> mmap flags */
+
+static bool
+phdr_in_zero_page(const Elf_Ehdr *hdr)
+{
+ return (hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr) <= page_size);
+}
+
+/*
+ * Map a shared object into memory. The "fd" argument is a file descriptor,
+ * which must be open on the object and positioned at its beginning.
+ * The "path" argument is a pathname that is used only for error messages.
+ *
+ * The return value is a pointer to a newly-allocated Obj_Entry structure
+ * for the shared object. Returns NULL on failure.
+ */
+Obj_Entry *
+map_object(int fd, const char *path, const struct stat *sb, bool ismain)
+{
+ Obj_Entry *obj;
+ Elf_Ehdr *hdr;
+ int i;
+ Elf_Phdr *phdr;
+ Elf_Phdr *phlimit;
+ Elf_Phdr **segs;
+ int nsegs;
+ Elf_Phdr *phdyn;
+ Elf_Phdr *phinterp;
+ Elf_Phdr *phtls;
+ caddr_t mapbase;
+ size_t mapsize;
+ Elf_Addr base_vaddr;
+ Elf_Addr base_vlimit;
+ caddr_t base_addr;
+ int base_flags;
+ Elf_Off data_offset;
+ Elf_Addr data_vaddr;
+ Elf_Addr data_vlimit;
+ caddr_t data_addr;
+ int data_prot;
+ int data_flags;
+ Elf_Addr clear_vaddr;
+ caddr_t clear_addr;
+ caddr_t clear_page;
+ Elf_Addr phdr_vaddr;
+ size_t nclear, phsize;
+ Elf_Addr bss_vaddr;
+ Elf_Addr bss_vlimit;
+ caddr_t bss_addr;
+ Elf_Word stack_flags;
+ Elf_Addr note_start;
+ Elf_Addr note_end;
+ char *note_map;
+ size_t note_map_len;
+ Elf_Addr text_end;
+
+ hdr = get_elf_header(fd, path, sb, &phdr);
+ if (hdr == NULL)
+ return (NULL);
+
+ /*
+ * Scan the program header entries, and save key information.
+ * We expect that the loadable segments are ordered by load address.
+ */
+ phsize = hdr->e_phnum * sizeof(phdr[0]);
+ phlimit = phdr + hdr->e_phnum;
+ nsegs = -1;
+ phdyn = phinterp = phtls = NULL;
+ phdr_vaddr = 0;
+ note_start = 0;
+ note_end = 0;
+ note_map = NULL;
+ note_map_len = 0;
+ segs = alloca(sizeof(segs[0]) * hdr->e_phnum);
+ stack_flags = PF_X | PF_R | PF_W;
+ text_end = 0;
+ while (phdr < phlimit) {
+ switch (phdr->p_type) {
+ case PT_INTERP:
+ phinterp = phdr;
+ break;
+
+ case PT_LOAD:
+ segs[++nsegs] = phdr;
+ if ((segs[nsegs]->p_align & (page_size - 1)) != 0) {
+ _rtld_error(
+ "%s: PT_LOAD segment %d not page-aligned",
+ path, nsegs);
+ goto error;
+ }
+ if ((segs[nsegs]->p_flags & PF_X) == PF_X) {
+ text_end = MAX(text_end,
+ rtld_round_page(segs[nsegs]->p_vaddr +
+ segs[nsegs]->p_memsz));
+ }
+ break;
+
+ case PT_PHDR:
+ phdr_vaddr = phdr->p_vaddr;
+ phsize = phdr->p_memsz;
+ break;
+
+ case PT_DYNAMIC:
+ phdyn = phdr;
+ break;
+
+ case PT_TLS:
+ phtls = phdr;
+ break;
+
+ case PT_GNU_STACK:
+ stack_flags = phdr->p_flags;
+ break;
+
+ case PT_NOTE:
+ if (phdr->p_offset > page_size ||
+ phdr->p_offset + phdr->p_filesz > page_size) {
+ note_map_len = rtld_round_page(phdr->p_offset +
+ phdr->p_filesz) -
+ rtld_trunc_page(phdr->p_offset);
+ note_map = mmap(NULL, note_map_len, PROT_READ,
+ MAP_PRIVATE, fd,
+ rtld_trunc_page(phdr->p_offset));
+ if (note_map == MAP_FAILED) {
+ _rtld_error(
+ "%s: error mapping PT_NOTE (%d)",
+ path, errno);
+ goto error;
+ }
+ note_start = (Elf_Addr)(note_map +
+ phdr->p_offset -
+ rtld_trunc_page(phdr->p_offset));
+ } else {
+ note_start = (Elf_Addr)(char *)hdr +
+ phdr->p_offset;
+ }
+ note_end = note_start + phdr->p_filesz;
+ break;
+ }
+
+ ++phdr;
+ }
+ if (phdyn == NULL) {
+ _rtld_error("%s: object is not dynamically-linked", path);
+ goto error;
+ }
+
+ if (nsegs < 0) {
+ _rtld_error("%s: too few PT_LOAD segments", path);
+ goto error;
+ }
+
+ /*
+ * Map the entire address space of the object, to stake out our
+ * contiguous region, and to establish the base address for relocation.
+ */
+ base_vaddr = rtld_trunc_page(segs[0]->p_vaddr);
+ base_vlimit = rtld_round_page(segs[nsegs]->p_vaddr +
+ segs[nsegs]->p_memsz);
+ mapsize = base_vlimit - base_vaddr;
+ base_addr = (caddr_t)base_vaddr;
+ base_flags = MAP_GUARD;
+ if (npagesizes > 1 && rtld_round_page(segs[0]->p_filesz) >=
+ pagesizes[1])
+ base_flags |= MAP_ALIGNED_SUPER;
+ if (base_vaddr != 0)
+ base_flags |= MAP_FIXED | MAP_EXCL;
+
+ mapbase = mmap(base_addr, mapsize, PROT_NONE, base_flags, -1, 0);
+ if (mapbase == MAP_FAILED) {
+ _rtld_error("%s: mmap of entire address space failed: %s",
+ path, rtld_strerror(errno));
+ goto error;
+ }
+ if (base_addr != NULL && mapbase != base_addr) {
+ _rtld_error(
+ "%s: mmap returned wrong address: wanted %p, got %p",
+ path, base_addr, mapbase);
+ goto error1;
+ }
+
+ for (i = 0; i <= nsegs; i++) {
+ /* Overlay the segment onto the proper region. */
+ data_offset = rtld_trunc_page(segs[i]->p_offset);
+ data_vaddr = rtld_trunc_page(segs[i]->p_vaddr);
+ data_vlimit = rtld_round_page(segs[i]->p_vaddr +
+ segs[i]->p_filesz);
+ data_addr = mapbase + (data_vaddr - base_vaddr);
+ data_prot = convert_prot(segs[i]->p_flags);
+ data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED;
+ if (data_vlimit != data_vaddr && mmap(data_addr,
+ data_vlimit - data_vaddr, data_prot, data_flags |
+ MAP_PREFAULT_READ, fd, data_offset) == MAP_FAILED) {
+ _rtld_error("%s: mmap of data failed: %s",
+ path, rtld_strerror(errno));
+ goto error1;
+ }
+
+ /* Do BSS setup */
+ if (segs[i]->p_filesz != segs[i]->p_memsz) {
+ /* Clear any BSS in the last page of the segment. */
+ clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz;
+ clear_addr = mapbase + (clear_vaddr - base_vaddr);
+ clear_page = mapbase + (rtld_trunc_page(clear_vaddr) -
+ base_vaddr);
+
+ if ((nclear = data_vlimit - clear_vaddr) > 0) {
+ /*
+ * Make sure the end of the segment is
+ * writable.
+ */
+ if ((data_prot & PROT_WRITE) == 0 &&
+ mprotect(clear_page, page_size,
+ data_prot | PROT_WRITE) == -1) {
+ _rtld_error("%s: mprotect failed: %s",
+ path, rtld_strerror(errno));
+ goto error1;
+ }
+
+ memset(clear_addr, 0, nclear);
+
+ /* Reset the data protection back */
+ if ((data_prot & PROT_WRITE) == 0)
+ mprotect(clear_page, page_size,
+ data_prot);
+ }
+
+ /* Overlay the BSS segment onto the proper region. */
+ bss_vaddr = data_vlimit;
+ bss_vlimit = rtld_round_page(segs[i]->p_vaddr +
+ segs[i]->p_memsz);
+ bss_addr = mapbase + (bss_vaddr - base_vaddr);
+ if (bss_vlimit > bss_vaddr) {
+ /* There is something to do */
+ if (mmap(bss_addr, bss_vlimit - bss_vaddr,
+ data_prot, data_flags | MAP_ANON, -1,
+ 0) == MAP_FAILED) {
+ _rtld_error(
+ "%s: mmap of bss failed: %s",
+ path, rtld_strerror(errno));
+ goto error1;
+ }
+ }
+ }
+
+ if (phdr_vaddr == 0 && data_offset <= hdr->e_phoff &&
+ data_vlimit - data_vaddr + data_offset >=
+ hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr)) {
+ phdr_vaddr = data_vaddr + hdr->e_phoff - data_offset;
+ }
+ }
+
+ obj = obj_new();
+ if (sb != NULL) {
+ obj->dev = sb->st_dev;
+ obj->ino = sb->st_ino;
+ }
+ obj->mapbase = mapbase;
+ obj->mapsize = mapsize;
+ obj->vaddrbase = base_vaddr;
+ obj->relocbase = mapbase - base_vaddr;
+ obj->dynamic = (const Elf_Dyn *)(obj->relocbase + phdyn->p_vaddr);
+ if (hdr->e_entry != 0)
+ obj->entry = (caddr_t)(obj->relocbase + hdr->e_entry);
+ if (phdr_vaddr != 0) {
+ obj->phdr = (const Elf_Phdr *)(obj->relocbase + phdr_vaddr);
+ } else {
+ obj->phdr = malloc(phsize);
+ if (obj->phdr == NULL) {
+ obj_free(obj);
+ _rtld_error("%s: cannot allocate program header",
+ path);
+ goto error1;
+ }
+ memcpy(__DECONST(char *, obj->phdr), (char *)hdr + hdr->e_phoff,
+ phsize);
+ obj->phdr_alloc = true;
+ }
+ obj->phsize = phsize;
+ if (phinterp != NULL)
+ obj->interp = (const char *)(obj->relocbase +
+ phinterp->p_vaddr);
+ if (phtls != NULL) {
+ if (ismain)
+ obj->tlsindex = 1;
+ else {
+ tls_dtv_generation++;
+ obj->tlsindex = ++tls_max_index;
+ }
+ obj->tlssize = phtls->p_memsz;
+ obj->tlsalign = phtls->p_align;
+ obj->tlspoffset = phtls->p_offset;
+ obj->tlsinitsize = phtls->p_filesz;
+ obj->tlsinit = obj->relocbase + phtls->p_vaddr;
+ }
+ obj->stack_flags = stack_flags;
+ if (note_start < note_end)
+ digest_notes(obj, note_start, note_end);
+ if (note_map != NULL)
+ munmap(note_map, note_map_len);
+ munmap(hdr, page_size);
+ return (obj);
+
+error1:
+ munmap(mapbase, mapsize);
+error:
+ if (note_map != NULL && note_map != MAP_FAILED)
+ munmap(note_map, note_map_len);
+ if (!phdr_in_zero_page(hdr))
+ munmap(phdr, hdr->e_phnum * sizeof(phdr[0]));
+ munmap(hdr, page_size);
+ return (NULL);
+}
+
+bool
+check_elf_headers(const Elf_Ehdr *hdr, const char *path)
+{
+ if (!IS_ELF(*hdr)) {
+ _rtld_error("%s: invalid file format", path);
+ return (false);
+ }
+ if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
+ hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
+ _rtld_error("%s: unsupported file layout", path);
+ return (false);
+ }
+ if (hdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ hdr->e_version != EV_CURRENT) {
+ _rtld_error("%s: unsupported file version", path);
+ return (false);
+ }
+ if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) {
+ _rtld_error("%s: unsupported file type", path);
+ return (false);
+ }
+ if (hdr->e_machine != ELF_TARG_MACH) {
+ _rtld_error("%s: unsupported machine", path);
+ return (false);
+ }
+ if (hdr->e_phentsize != sizeof(Elf_Phdr)) {
+ _rtld_error(
+ "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)",
+ path);
+ return (false);
+ }
+ return (true);
+}
+
+static Elf_Ehdr *
+get_elf_header(int fd, const char *path, const struct stat *sbp,
+ Elf_Phdr **phdr_p)
+{
+ Elf_Ehdr *hdr;
+ Elf_Phdr *phdr;
+
+ /* Make sure file has enough data for the ELF header */
+ if (sbp != NULL && sbp->st_size < (off_t)sizeof(Elf_Ehdr)) {
+ _rtld_error("%s: invalid file format", path);
+ return (NULL);
+ }
+
+ hdr = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ,
+ fd, 0);
+ if (hdr == MAP_FAILED) {
+ _rtld_error("%s: read error: %s", path, rtld_strerror(errno));
+ return (NULL);
+ }
+
+ /* Make sure the file is valid */
+ if (!check_elf_headers(hdr, path))
+ goto error;
+
+ /*
+ * We rely on the program header being in the first page. This is
+ * not strictly required by the ABI specification, but it seems to
+ * always true in practice. And, it simplifies things considerably.
+ */
+ if (phdr_in_zero_page(hdr)) {
+ phdr = (Elf_Phdr *)((char *)hdr + hdr->e_phoff);
+ } else {
+ phdr = mmap(NULL, hdr->e_phnum * sizeof(phdr[0]), PROT_READ,
+ MAP_PRIVATE | MAP_PREFAULT_READ, fd, hdr->e_phoff);
+ if (phdr == MAP_FAILED) {
+ _rtld_error("%s: error mapping phdr: %s", path,
+ rtld_strerror(errno));
+ goto error;
+ }
+ }
+ *phdr_p = phdr;
+ return (hdr);
+
+error:
+ munmap(hdr, page_size);
+ return (NULL);
+}
+
+void
+obj_free(Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ if (obj->tls_static)
+ free_tls_offset(obj);
+ while (obj->needed != NULL) {
+ Needed_Entry *needed = obj->needed;
+
+ obj->needed = needed->next;
+ free(needed);
+ }
+ while (!STAILQ_EMPTY(&obj->names)) {
+ Name_Entry *entry = STAILQ_FIRST(&obj->names);
+
+ STAILQ_REMOVE_HEAD(&obj->names, link);
+ free(entry);
+ }
+ while (!STAILQ_EMPTY(&obj->dldags)) {
+ elm = STAILQ_FIRST(&obj->dldags);
+ STAILQ_REMOVE_HEAD(&obj->dldags, link);
+ free(elm);
+ }
+ while (!STAILQ_EMPTY(&obj->dagmembers)) {
+ elm = STAILQ_FIRST(&obj->dagmembers);
+ STAILQ_REMOVE_HEAD(&obj->dagmembers, link);
+ free(elm);
+ }
+ if (obj->vertab)
+ free(obj->vertab);
+ if (obj->origin_path)
+ free(obj->origin_path);
+ if (obj->z_origin)
+ free(__DECONST(void *, obj->rpath));
+ if (obj->priv)
+ free(obj->priv);
+ if (obj->path)
+ free(obj->path);
+ if (obj->phdr_alloc)
+ free(__DECONST(void *, obj->phdr));
+ free(obj);
+}
+
+Obj_Entry *
+obj_new(void)
+{
+ Obj_Entry *obj;
+
+ obj = CNEW(Obj_Entry);
+ STAILQ_INIT(&obj->dldags);
+ STAILQ_INIT(&obj->dagmembers);
+ STAILQ_INIT(&obj->names);
+ return (obj);
+}
+
+/*
+ * Given a set of ELF protection flags, return the corresponding protection
+ * flags for MMAP.
+ */
+int
+convert_prot(int elfflags)
+{
+ int prot = 0;
+
+ if ((elfflags & PF_R) != 0)
+ prot |= PROT_READ;
+ if ((elfflags & PF_W) != 0)
+ prot |= PROT_WRITE;
+ if ((elfflags & PF_X) != 0)
+ prot |= PROT_EXEC;
+ return (prot);
+}
+
+static int
+convert_flags(int elfflags)
+{
+ int flags = MAP_PRIVATE; /* All mappings are private */
+
+ /*
+ * Readonly mappings are marked "MAP_NOCORE", because they can be
+ * reconstructed by a debugger.
+ */
+ if ((elfflags & PF_W) == 0)
+ flags |= MAP_NOCORE;
+ return (flags);
+}
diff --git a/libexec/rtld-elf/powerpc/Makefile.inc b/libexec/rtld-elf/powerpc/Makefile.inc
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/libexec/rtld-elf/powerpc/Makefile.inc
diff --git a/libexec/rtld-elf/powerpc/reloc.c b/libexec/rtld-elf/powerpc/reloc.c
new file mode 100644
index 000000000000..8932c2c21278
--- /dev/null
+++ b/libexec/rtld-elf/powerpc/reloc.c
@@ -0,0 +1,842 @@
+/* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 1998 Tsubai Masanari
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/cpu.h>
+#include <machine/atomic.h>
+#include <machine/md_var.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+#define _ppc_ha(x) ((((u_int32_t)(x) & 0x8000) ? \
+ ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16)
+#define _ppc_la(x) ((u_int32_t)(x) & 0xffff)
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+#define PLT_EXTENDED_BEGIN (1 << 13)
+#define JMPTAB_BASE(N) (18 + N*2 + ((N > PLT_EXTENDED_BEGIN) ? \
+ (N - PLT_EXTENDED_BEGIN)*2 : 0))
+
+void _rtld_bind_secureplt_start(void);
+
+bool
+arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp)
+{
+ if (dynp->d_tag == DT_PPC_GOT) {
+ obj->gotptr = (Elf_Addr *)(obj->relocbase + dynp->d_un.d_ptr);
+ return (true);
+ }
+
+ return (false);
+}
+
+/*
+ * Process the R_PPC_COPY relocations
+ */
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ /*
+ * COPY relocs are invalid outside of the main program
+ */
+ assert(dstobj->mainprog);
+
+ relalim = (const Elf_Rela *)((const char *) dstobj->rela +
+ dstobj->relasize);
+ for (rela = dstobj->rela; rela < relalim; rela++) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym = NULL;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) {
+ continue;
+ }
+
+ dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = globallist_next(dstobj); srcobj != NULL;
+ srcobj = globallist_next(srcobj)) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+
+ if (srcobj == NULL) {
+ _rtld_error("Undefined symbol \"%s\" "
+ " referenced from COPY"
+ " relocation in %s", name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase+srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ dbg("copy_reloc: src=%p,dst=%p,size=%d\n",srcaddr,dstaddr,size);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Perform early relocation of the run-time linker image
+ */
+void
+reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
+{
+ const Elf_Rela *rela = NULL, *relalim;
+ Elf_Addr relasz = 0;
+ Elf_Addr *where;
+
+ /*
+ * Extract the rela/relasz values from the dynamic section
+ */
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_RELA:
+ rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ relasz = dynp->d_un.d_val;
+ break;
+ }
+ }
+
+ /*
+ * Relocate these values
+ */
+ relalim = (const Elf_Rela *)((const char *)rela + relasz);
+ for (; rela < relalim; rela++) {
+ where = (Elf_Addr *)(relocbase + rela->r_offset);
+ *where = (Elf_Addr)(relocbase + rela->r_addend);
+ }
+}
+
+
+/*
+ * Relocate a non-PLT object with addend.
+ */
+static int
+reloc_nonplt_object(Obj_Entry *obj_rtld __unused, Obj_Entry *obj,
+ const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate)
+{
+ const Elf_Sym *def = NULL;
+ const Obj_Entry *defobj;
+ Elf_Addr *where, symval = 0;
+
+ /*
+ * First, resolve symbol for relocations which
+ * reference symbols.
+ */
+ switch (ELF_R_TYPE(rela->r_info)) {
+
+ case R_PPC_UADDR32: /* word32 S + A */
+ case R_PPC_ADDR32:
+ case R_PPC_GLOB_DAT: /* word32 S + A */
+ case R_PPC_DTPMOD32:
+ case R_PPC_TPREL32:
+ case R_PPC_DTPREL32:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+ if (def == NULL) {
+ return (-1);
+ }
+
+ /*
+ * If symbol is IFUNC, only perform relocation
+ * when caller allowed it by passing
+ * SYMLOOK_IFUNC flag. Skip the relocations
+ * otherwise.
+ *
+ * Also error out in case IFUNC relocations
+ * are specified for TLS, which cannot be
+ * usefully interpreted.
+ */
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_PPC_UADDR32:
+ case R_PPC_ADDR32:
+ case R_PPC_GLOB_DAT:
+ if ((flags & SYMLOOK_IFUNC) == 0) {
+ dbg("Non-PLT reference to IFUNC found!");
+ obj->non_plt_gnu_ifunc = true;
+ return (0);
+ }
+ symval = (Elf_Addr)rtld_resolve_ifunc(
+ defobj, def);
+ break;
+ default:
+ _rtld_error("%s: IFUNC for TLS reloc",
+ obj->path);
+ return (-1);
+ }
+ } else {
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ return (0);
+ symval = (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ }
+ break;
+ default:
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ return (0);
+ }
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_PPC_NONE:
+ break;
+ case R_PPC_UADDR32:
+ case R_PPC_ADDR32:
+ case R_PPC_GLOB_DAT:
+ /* Don't issue write if unnecessary; avoid COW page fault */
+ if (*where != symval + rela->r_addend) {
+ *where = symval + rela->r_addend;
+ }
+ break;
+ case R_PPC_DTPMOD32:
+ *where = (Elf_Addr) defobj->tlsindex;
+ break;
+ case R_PPC_TPREL32:
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_static) {
+ if (!allocate_tls_offset(
+ __DECONST(Obj_Entry *, defobj))) {
+ _rtld_error("%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ return (-1);
+ }
+ }
+
+ *(Elf_Addr **)where = *where * sizeof(Elf_Addr)
+ + (Elf_Addr *)(def->st_value + rela->r_addend
+ + defobj->tlsoffset - TLS_TP_OFFSET - TLS_TCB_SIZE);
+ break;
+ case R_PPC_DTPREL32:
+ *where += (Elf_Addr)(def->st_value + rela->r_addend
+ - TLS_DTV_OFFSET);
+ break;
+ case R_PPC_RELATIVE: /* word32 B + A */
+ symval = (Elf_Addr)(obj->relocbase + rela->r_addend);
+
+ /* As above, don't issue write unnecessarily */
+ if (*where != symval) {
+ *where = symval;
+ }
+ break;
+ case R_PPC_COPY:
+ /*
+ * These are deferred until all other relocations
+ * have been done. All we do here is make sure
+ * that the COPY relocation is not in a shared
+ * library. They are allowed only in executable
+ * files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_COPY "
+ " relocation in shared library",
+ obj->path);
+ return (-1);
+ }
+ break;
+ case R_PPC_IRELATIVE:
+ /*
+ * These will be handled by reloc_iresolve().
+ */
+ obj->irelative = true;
+ break;
+ case R_PPC_JMP_SLOT:
+ /*
+ * These will be handled by the plt/jmpslot routines
+ */
+ break;
+
+ default:
+ _rtld_error("%s: Unsupported relocation type %d"
+ " in non-PLT relocations\n", obj->path,
+ ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ return (0);
+}
+
+
+/*
+ * Process non-PLT relocations
+ */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Phdr *phdr;
+ SymCache *cache;
+ int r = -1;
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj != obj_rtld) {
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+ } else
+ cache = NULL;
+
+ /*
+ * From the SVR4 PPC ABI:
+ * "The PowerPC family uses only the Elf32_Rela relocation
+ * entries with explicit addends."
+ */
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags,
+ lockstate) < 0)
+ goto done;
+ }
+ r = 0;
+done:
+ if (cache != NULL)
+ free(cache);
+
+ /*
+ * Synchronize icache for executable segments in case we made
+ * any changes.
+ */
+ for (phdr = obj->phdr;
+ (const char *)phdr < (const char *)obj->phdr + obj->phsize;
+ phdr++) {
+ if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X) != 0) {
+ __syncicache(obj->relocbase + phdr->p_vaddr,
+ phdr->p_memsz);
+ }
+ }
+
+ return (r);
+}
+
+/*
+ * Initialise a PLT slot to the resolving trampoline
+ */
+static int
+reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela)
+{
+ Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
+ Elf_Addr *pltresolve, *pltlongresolve, *jmptab;
+ Elf_Addr distance;
+ int N = obj->pltrelasize / sizeof(Elf_Rela);
+ int reloff;
+
+ reloff = rela - obj->pltrela;
+
+ if (reloff < 0)
+ return (-1);
+
+ if (obj->gotptr != NULL) {
+ *where += (Elf_Addr)obj->relocbase;
+ return (0);
+ }
+
+ pltlongresolve = obj->pltgot + 5;
+ pltresolve = pltlongresolve + 5;
+
+ distance = (Elf_Addr)pltresolve - (Elf_Addr)(where + 1);
+
+ dbg(" reloc_plt_object: where=%p,pltres=%p,reloff=%x,distance=%x",
+ (void *)where, (void *)pltresolve, reloff, distance);
+
+ if (reloff < PLT_EXTENDED_BEGIN) {
+ /* li r11,reloff */
+ /* b pltresolve */
+ where[0] = 0x39600000 | reloff;
+ where[1] = 0x48000000 | (distance & 0x03fffffc);
+ } else {
+ jmptab = obj->pltgot + JMPTAB_BASE(N);
+ jmptab[reloff] = (u_int)pltlongresolve;
+
+ /* lis r11,jmptab[reloff]@ha */
+ /* lwzu r12,jmptab[reloff]@l(r11) */
+ /* mtctr r12 */
+ /* bctr */
+ where[0] = 0x3d600000 | _ppc_ha(&jmptab[reloff]);
+ where[1] = 0x858b0000 | _ppc_la(&jmptab[reloff]);
+ where[2] = 0x7d8903a6;
+ where[3] = 0x4e800420;
+ }
+
+
+ /*
+ * The icache will be sync'd in reloc_plt, which is called
+ * after all the slots have been updated
+ */
+
+ return (0);
+}
+
+/*
+ * Process the PLT relocations.
+ */
+int
+reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ int N = obj->pltrelasize / sizeof(Elf_Rela);
+
+ if (obj->pltrelasize != 0) {
+
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {
+ dbg("ABI violation - found IRELATIVE in the PLT.");
+ obj->irelative = true;
+ continue;
+ }
+
+ /*
+ * PowerPC(64) .rela.plt is composed of an array of
+ * R_PPC_JMP_SLOT relocations. Unlike other platforms,
+ * this is the ONLY relocation type that is valid here.
+ */
+ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
+
+ if (reloc_plt_object(obj, rela) < 0) {
+ return (-1);
+ }
+ }
+ }
+
+ /*
+ * Sync the icache for the byte range represented by the
+ * trampoline routines and call slots.
+ */
+ if (obj->pltgot != NULL && obj->gotptr == NULL)
+ __syncicache(obj->pltgot, JMPTAB_BASE(N)*4);
+
+ return (0);
+}
+
+/*
+ * LD_BIND_NOW was set - force relocation for all jump slots
+ */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+ Elf_Addr *where;
+ Elf_Addr target;
+
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ /* This isn't actually a jump slot, ignore it. */
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE)
+ continue;
+ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL) {
+ dbg("reloc_jmpslots: sym not found");
+ return (-1);
+ }
+
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+
+ if (def == &sym_zero) {
+ /* Zero undefined weak symbols */
+ *where = 0;
+ } else {
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ /* LD_BIND_NOW, ifunc in shared lib.*/
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *) rela);
+ }
+ }
+
+ obj->jmpslots_done = true;
+
+ return (0);
+}
+
+
+/*
+ * Update the value of a PLT jump slot.
+ */
+Elf_Addr
+reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target,
+ const Obj_Entry *defobj __unused, const Obj_Entry *obj, const Elf_Rel *rel)
+{
+ Elf_Addr offset;
+ const Elf_Rela *rela = (const Elf_Rela *) rel;
+
+ dbg(" reloc_jmpslot: where=%p, target=%p",
+ (void *)wherep, (void *)target);
+
+ if (ld_bind_not)
+ goto out;
+
+
+ /*
+ * Process Secure-PLT.
+ */
+ if (obj->gotptr != NULL) {
+ assert(wherep >= (Elf_Word *)obj->pltgot);
+ assert(wherep <
+ (Elf_Word *)obj->pltgot + obj->pltrelasize);
+ if (*wherep != target)
+ *wherep = target;
+ goto out;
+ }
+
+ /*
+ * BSS-PLT optimization:
+ * Branch directly to the target if it is within +/- 32Mb,
+ * otherwise go indirectly via the pltcall trampoline call and
+ * jump table.
+ */
+ offset = target - (Elf_Addr)wherep;
+ if (abs((int)offset) < 32*1024*1024) { /* inside 32MB? */
+ /*
+ * At the PLT entry pointed at by `wherep', construct
+ * a direct transfer to the now fully resolved function
+ * address.
+ */
+ /* b value # branch directly */
+ *wherep = 0x48000000 | (offset & 0x03fffffc);
+ __syncicache(wherep, 4);
+ } else {
+ Elf_Addr *pltcall, *jmptab;
+ int distance;
+ int N = obj->pltrelasize / sizeof(Elf_Rela);
+ int reloff = rela - obj->pltrela;
+
+ if (reloff < 0)
+ return (-1);
+
+ pltcall = obj->pltgot;
+
+ dbg(" reloc_jmpslot: indir, reloff=%x, N=%x\n",
+ reloff, N);
+
+ jmptab = obj->pltgot + JMPTAB_BASE(N);
+ jmptab[reloff] = target;
+ mb(); /* Order jmptab update before next changes */
+
+ if (reloff < PLT_EXTENDED_BEGIN) {
+ /* for extended PLT entries, we keep the old code */
+
+ distance = (Elf_Addr)pltcall - (Elf_Addr)(wherep + 1);
+
+ /* li r11,reloff */
+ /* b pltcall # use indirect pltcall routine */
+
+ /* first instruction same as before */
+ wherep[1] = 0x48000000 | (distance & 0x03fffffc);
+ __syncicache(wherep, 8);
+ }
+ }
+
+out:
+ return (target);
+}
+
+int
+reloc_iresolve(Obj_Entry *obj,
+ struct Struct_RtldLockState *lockstate)
+{
+ /*
+ * Since PLT slots on PowerPC are always R_PPC_JMP_SLOT,
+ * R_PPC_IRELATIVE is in RELA.
+ */
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ Elf_Addr *where, target, *ptr;
+
+ if (!obj->irelative)
+ return (0);
+
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {
+ ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+
+ lock_release(rtld_bind_lock, lockstate);
+ target = call_ifunc_resolver(ptr);
+ wlock_acquire(rtld_bind_lock, lockstate);
+
+ *where = target;
+ }
+ }
+ /*
+ * XXX Remove me when lld is fixed!
+ * LLD currently makes illegal relocations in the PLT.
+ */
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {
+ ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+
+ lock_release(rtld_bind_lock, lockstate);
+ target = call_ifunc_resolver(ptr);
+ wlock_acquire(rtld_bind_lock, lockstate);
+
+ *where = target;
+ }
+ }
+
+ obj->irelative = false;
+ return (0);
+}
+
+int
+reloc_iresolve_nonplt(Obj_Entry *obj __unused,
+ struct Struct_RtldLockState *lockstate __unused)
+{
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused,
+ struct Struct_RtldLockState *lockstate __unused)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT) {
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+ lock_release(rtld_bind_lock, lockstate);
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *)rela);
+ }
+ }
+ obj->gnu_ifunc = false;
+ return (0);
+}
+
+/*
+ * Setup the plt glue routines.
+ */
+#define PLTCALL_SIZE 20
+#define PLTLONGRESOLVE_SIZE 20
+#define PLTRESOLVE_SIZE 24
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+ Elf_Word *pltcall, *pltresolve, *pltlongresolve;
+ Elf_Word *jmptab;
+ int N = obj->pltrelasize / sizeof(Elf_Rela);
+
+ pltcall = obj->pltgot;
+
+ if (pltcall == NULL) {
+ return;
+ }
+
+ /* Handle Secure-PLT first, if applicable. */
+ if (obj->gotptr != NULL) {
+ obj->gotptr[1] = (Elf_Addr)_rtld_bind_secureplt_start;
+ obj->gotptr[2] = (Elf_Addr)obj;
+ dbg("obj %s secure-plt gotptr=%p start=%p obj=%p",
+ obj->path, obj->gotptr,
+ (void *)obj->gotptr[1], (void *)obj->gotptr[2]);
+ return;
+ }
+
+ /*
+ * From the SVR4 PPC ABI:
+ *
+ * 'The first 18 words (72 bytes) of the PLT are reserved for
+ * use by the dynamic linker.
+ * ...
+ * 'If the executable or shared object requires N procedure
+ * linkage table entries, the link editor shall reserve 3*N
+ * words (12*N bytes) following the 18 reserved words. The
+ * first 2*N of these words are the procedure linkage table
+ * entries themselves. The static linker directs calls to bytes
+ * (72 + (i-1)*8), for i between 1 and N inclusive. The remaining
+ * N words (4*N bytes) are reserved for use by the dynamic linker.'
+ */
+
+ /*
+ * Copy the absolute-call assembler stub into the first part of
+ * the reserved PLT area.
+ */
+ memcpy(pltcall, _rtld_powerpc_pltcall, PLTCALL_SIZE);
+
+ /*
+ * Determine the address of the jumptable, which is the dyn-linker
+ * reserved area after the call cells. Write the absolute address
+ * of the jumptable into the absolute-call assembler code so it
+ * can determine this address.
+ */
+ jmptab = obj->pltgot + JMPTAB_BASE(N);
+ pltcall[1] |= _ppc_ha(jmptab); /* addis 11,11,jmptab@ha */
+ pltcall[2] |= _ppc_la(jmptab); /* lwz 11,jmptab@l(11) */
+
+ /*
+ * Skip down 20 bytes into the initial reserved area and copy
+ * in the standard resolving assembler call. Into this assembler,
+ * insert the absolute address of the _rtld_bind_start routine
+ * and the address of the relocation object.
+ *
+ * We place pltlongresolve first, so it can fix up its arguments
+ * and then fall through to the regular PLT resolver.
+ */
+ pltlongresolve = obj->pltgot + 5;
+
+ memcpy(pltlongresolve, _rtld_powerpc_pltlongresolve,
+ PLTLONGRESOLVE_SIZE);
+ pltlongresolve[0] |= _ppc_ha(jmptab); /* lis 12,jmptab@ha */
+ pltlongresolve[1] |= _ppc_la(jmptab); /* addi 12,12,jmptab@l */
+
+ pltresolve = pltlongresolve + PLTLONGRESOLVE_SIZE/sizeof(uint32_t);
+ memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE);
+ pltresolve[0] |= _ppc_ha(_rtld_bind_start);
+ pltresolve[1] |= _ppc_la(_rtld_bind_start);
+ pltresolve[3] |= _ppc_ha(obj);
+ pltresolve[4] |= _ppc_la(obj);
+
+ /*
+ * The icache will be sync'd in reloc_plt, which is called
+ * after all the slots have been updated
+ */
+}
+
+/*
+ * 32 bit cpu feature flag fields.
+ */
+u_long cpu_features;
+u_long cpu_features2;
+
+void
+powerpc_abi_variant_hook(Elf_Auxinfo** aux_info)
+{
+ /*
+ * Since aux_info[] is easier to work with than aux, go ahead and
+ * initialize cpu_features / cpu_features2.
+ */
+ cpu_features = -1UL;
+ cpu_features2 = -1UL;
+ if (aux_info[AT_HWCAP] != NULL)
+ cpu_features = aux_info[AT_HWCAP]->a_un.a_val;
+ if (aux_info[AT_HWCAP2] != NULL)
+ cpu_features2 = aux_info[AT_HWCAP2]->a_un.a_val;
+}
+
+void
+ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)
+{
+
+}
+
+void
+allocate_initial_tls(Obj_Entry *list)
+{
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+
+ tls_static_space = tls_last_offset + tls_last_size +
+ ld_static_tls_extra;
+
+ _tcb_set(allocate_tls(list, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN));
+}
+
+void*
+__tls_get_addr(tls_index* ti)
+{
+ return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset +
+ TLS_DTV_OFFSET));
+}
diff --git a/libexec/rtld-elf/powerpc/rtld_machdep.h b/libexec/rtld-elf/powerpc/rtld_machdep.h
new file mode 100644
index 000000000000..ec470f238991
--- /dev/null
+++ b/libexec/rtld-elf/powerpc/rtld_machdep.h
@@ -0,0 +1,101 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+#include <machine/tls.h>
+
+struct Struct_Obj_Entry;
+
+#define MD_OBJ_ENTRY \
+ Elf_Addr *gotptr; /* GOT pointer (secure-plt only) */
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) (&_DYNAMIC)
+
+bool arch_digest_dynamic(struct Struct_Obj_Entry *, const Elf_Dyn *);
+
+/* No architecture specific notes */
+#define arch_digest_note(obj, note) false
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj,
+ const Elf_Rel *rel);
+void reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+extern u_long cpu_features; /* r3 */
+extern u_long cpu_features2; /* r4 */
+/* r5-10: ifunc resolver parameters reserved for future assignment. */
+#define call_ifunc_resolver(ptr) \
+ (((Elf_Addr (*)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, \
+ uint32_t, uint32_t, uint32_t))ptr)((uint32_t)cpu_features, \
+ (uint32_t)cpu_features2, 0, 0, 0, 0, 0, 0))
+
+/*
+ * PLT functions. Not really correct prototypes, but the
+ * symbol values are needed.
+ */
+void _rtld_powerpc_pltlongresolve(void);
+void _rtld_powerpc_pltresolve(void);
+void _rtld_powerpc_pltcall(void);
+
+/*
+ * TLS
+ */
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align, offset) \
+ TLS_TCB_SIZE
+#define calculate_tls_offset(prev_offset, prev_size, size, align, offset) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_post_size(align) 0
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index* ti);
+
+extern void powerpc_abi_variant_hook(Elf_Auxinfo **);
+#define md_abi_variant_hook(x) powerpc_abi_variant_hook(x)
+
+#endif
diff --git a/libexec/rtld-elf/powerpc/rtld_start.S b/libexec/rtld-elf/powerpc/rtld_start.S
new file mode 100644
index 000000000000..e34cc56fc40c
--- /dev/null
+++ b/libexec/rtld-elf/powerpc/rtld_start.S
@@ -0,0 +1,319 @@
+/* $NetBSD: rtld_start.S,v 1.4 2001/09/26 04:06:43 mycroft Exp $ */
+
+/*-
+ * Copyright (C) 1998 Tsubai Masanari
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#include <machine/spr.h> /* For SPR_SPEFSCR if needed. */
+
+.extern _GLOBAL_OFFSET_TABLE_
+.extern _DYNAMIC
+
+_ENTRY(.rtld_start)
+ stwu %r1,-48(%r1) /* 16-byte aligned stack for reg saves +
+ exit_proc & obj _rtld args +
+ backchain & lrsave stack frame */
+ stw %r3,16(%r1) /* argc */
+ stw %r4,20(%r1) /* argv */
+ stw %r5,24(%r1) /* envp */
+/* stw %r6,28(%r1) *//* obj (always 0) */
+/* stw %r7,32(%r1) *//* cleanup (always 0) */
+ stw %r8,36(%r1) /* ps_strings */
+
+ /*
+ * Perform initial relocation of ld-elf.so. Not as easy as it
+ * sounds.
+ * - perform small forward branch to put PC into link reg
+ * - use link-time constants to determine offset to the
+ * _DYNAMIC section and the GOT. Add these to the PC to
+ * convert to absolute addresses.
+ * - read GOT[0], which is the SVR4 ABI-specified link-time
+ * value of _DYNAMIC. Subtract this value from the absolute
+ * value to determine the load address
+ * - call reloc_non_plt_self() to fix up ld-elf.so's relocations
+ */
+ bcl 20,31,1f
+1: mflr %r30
+ mr %r3,%r30 # save for _DYNAMIC
+ addis %r30,%r30,_GLOBAL_OFFSET_TABLE_-1b@ha
+ addi %r30,%r30,_GLOBAL_OFFSET_TABLE_-1b@l
+ addis %r3,%r3,_DYNAMIC-1b@ha # get _DYNAMIC actual address
+ addi %r3,%r3,_DYNAMIC-1b@l
+ lwz %r28,0(%r30) # get base-relative &_DYNAMIC
+ sub %r28,%r3,%r28 # r28 = relocbase
+ mr %r4,%r28 # r4 = relocbase
+ bl reloc_non_plt_self /* reloc_non_plt_self(&_DYNAMIC,base) */
+
+ /*
+ * The _rtld() function likes to see a stack layout containing
+ * { argc, argv[0], argv[1] ... argv[N], 0, env[0], ... , env[N] }
+ * Since the PowerPC stack was 16-byte aligned at exec time, the
+ * original stack layout has to be found by moving back a word
+ * from the argv pointer.
+ */
+ lwz %r4,20(%r1) /* restore argv */
+ addi %r3,%r4,-4 /* locate argc ptr, &argv[-1] */
+
+ addi %r4,%r1,8 /* &exit_proc on stack */
+ addi %r5,%r1,12 /* &obj_main on stack */
+
+ bl _rtld /* &_start = _rtld(sp, &exit_proc, &obj_main)*/
+ mtlr %r3
+
+ /*
+ * Restore args, with new obj/exit proc
+ */
+ lwz %r3,16(%r1) /* argc */
+ lwz %r4,20(%r1) /* argv */
+ lwz %r5,24(%r1) /* envp */
+ lwz %r6,12(%r1) /* obj */
+ lwz %r7,8(%r1) /* exit proc */
+ lwz %r8,36(%r1) /* ps_strings */
+ addi %r1,%r1,48 /* restore original stackptr */
+
+ blrl /* _start(argc, argv, envp, obj, cleanup, ps_strings) */
+
+ li %r0,1 /* _exit() */
+ sc
+_END(.rtld_start)
+
+#ifdef __SPE__
+/* stack space for 30 GPRs + SPEFSCR/ACC/lr/cr */
+#define NREGS 31
+#define GPRWIDTH 8
+#define FUDGE 4 /* Fudge factor for alignment */
+#else
+/* stack space for 30 GPRs + lr/cr */
+#define NREGS 30
+#define GPRWIDTH 4
+#define FUDGE 4
+#endif
+/* Stack frame needs the 12-byte ABI frame plus fudge factor. */
+#define STACK_SIZE (NREGS * GPRWIDTH + 4 * 2 + 12 + FUDGE)
+
+/*
+ * _rtld_bind_secureplt_start()
+ *
+ * Call into the MI binder (Secure-PLT stub).
+ * secure-plt expects %r11 to be the offset to the rela entry.
+ * bss-plt expects %r11 to be index of the rela entry.
+ * So for bss-plt, we multiply the index by 12 to get the offset.
+ */
+_ENTRY(_rtld_bind_secureplt_start)
+ stwu %r1,-STACK_SIZE(%r1)
+#ifdef __SPE__
+ evstdd %r0,24(%r1)
+#else
+ stw %r0,20(%r1) # save r0
+#endif
+
+ /*
+ * Instead of division which is costly we will use multiplicative
+ * inverse. a / n = ((a * inv(n)) >> 32)
+ * where inv(n) = (0x100000000 + n - 1) / n
+ */
+ mr %r0,%r11
+ lis %r11,0x15555556@h # load multiplicative inverse of 12
+ ori %r11,%r11,0x15555556@l
+ mulhwu %r11,%r11,%r0 # get high half of multiplication
+ b 1f
+_END(_rtld_bind_secureplt_start)
+
+/*
+ * _rtld_bind_start()
+ *
+ * Call into the MI binder. This routine is reached via the PLT call cell,
+ * and then _rtld_powerpc_pltresolve().
+ * On entry, %r11 contains the index of the PLT cell, and %r12 contains
+ * a pointer to the ELF object for the file.
+ * Save all registers, call into the binder to resolve and fixup the external
+ * routine, and then transfer to the external routine on return.
+ */
+ .globl _rtld_bind
+
+_ENTRY(_rtld_bind_start)
+ stwu %r1,-STACK_SIZE(%r1)
+#ifdef __SPE__
+ evstdd %r0,24(%r1)
+#else
+ stw %r0,20(%r1) # save r0
+#endif
+1:
+ mflr %r0
+ stw %r0,16(%r1) # save lr
+ mfcr %r0
+ stw %r0,12(%r1) # save cr
+#ifdef __SPE__
+ evstdd %r3, 32(%r1)
+ evstdd %r4, 40(%r1)
+ evstdd %r5, 48(%r1)
+ evstdd %r6, 56(%r1)
+ evstdd %r7, 64(%r1)
+ evstdd %r8, 72(%r1)
+ evstdd %r9, 80(%r1)
+ evstdd %r10, 88(%r1)
+ evstdd %r11, 96(%r1)
+ evstdd %r12, 104(%r1)
+ evstdd %r13, 112(%r1)
+ evstdd %r14, 120(%r1)
+ evstdd %r15, 128(%r1)
+ evstdd %r16, 136(%r1)
+ evstdd %r17, 144(%r1)
+ evstdd %r18, 152(%r1)
+ evstdd %r19, 160(%r1)
+ evstdd %r20, 168(%r1)
+ evstdd %r21, 176(%r1)
+ evstdd %r22, 184(%r1)
+ evstdd %r23, 192(%r1)
+ evstdd %r24, 200(%r1)
+ evstdd %r25, 208(%r1)
+ evstdd %r26, 216(%r1)
+ evstdd %r27, 224(%r1)
+ evstdd %r28, 232(%r1)
+ evstdd %r29, 240(%r1)
+ evstdd %r30, 248(%r1)
+ li %r3, 256
+ evstddx %r31, %r1, %r3
+ evxor %r0, %r0, %r0
+ li %r3, 264
+ evmwumiaa %r0, %r0, %r0
+ evstddx %r0, %r1, %r3
+ mfspr %r3, SPR_SPEFSCR
+ stw %r3, 20(%r1)
+#else
+ stmw %r3,24(%r1) # save r3-r31
+#endif
+
+ mr %r3,%r12 # obj
+ mulli %r4,%r11,12 # rela index * sizeof(Elf_Rela)
+ bl _rtld_bind # target addr = _rtld_bind(obj, reloff)
+ mtctr %r3 # move absolute target addr into ctr
+
+#ifdef __SPE__
+ lwz %r3, 20(%r1)
+ mtspr SPR_SPEFSCR, %r3
+ li %r3, 264
+ evlddx %r0, %r3, %r1
+ evmra %r0, %r0
+ evldd %r3, 32(%r1)
+ evldd %r4, 40(%r1)
+ evldd %r5, 48(%r1)
+ evldd %r6, 56(%r1)
+ evldd %r7, 64(%r1)
+ evldd %r8, 72(%r1)
+ evldd %r9, 80(%r1)
+ evldd %r10, 88(%r1)
+ evldd %r11, 96(%r1)
+ evldd %r12, 104(%r1)
+ evldd %r13, 112(%r1)
+ evldd %r14, 120(%r1)
+ evldd %r15, 128(%r1)
+ evldd %r16, 136(%r1)
+ evldd %r17, 144(%r1)
+ evldd %r18, 152(%r1)
+ evldd %r19, 160(%r1)
+ evldd %r20, 168(%r1)
+ evldd %r21, 176(%r1)
+ evldd %r22, 184(%r1)
+ evldd %r23, 192(%r1)
+ evldd %r24, 200(%r1)
+ evldd %r25, 208(%r1)
+ evldd %r26, 216(%r1)
+ evldd %r27, 224(%r1)
+ evldd %r28, 232(%r1)
+ evldd %r29, 240(%r1)
+ evldd %r30, 248(%r1)
+ li %r0, 256
+ evlddx %r31, %r1, %r0
+#else
+ lmw %r3,24(%r1) # restore r3-r31
+#endif
+ lwz %r0,12(%r1) # restore cr
+ mtcr %r0
+ lwz %r0,16(%r1) # restore lr
+ mtlr %r0
+#ifdef __SPE__
+ evldd %r0,24(%r1)
+#else
+ lwz %r0,20(%r1) # restore r0
+#endif
+
+ addi %r1,%r1,STACK_SIZE # restore stack
+ bctr # jump to target
+_END(_rtld_bind_start)
+
+
+/*
+ * _rtld_powerpc_pltresolve()
+ *
+ * This routine is copied into the latter part of the 72-byte reserved
+ * area at the start of the PLT. The absolute address of the _rtld_bind_start
+ * routine, and the ELF object for the loaded file, are inserted into
+ * the code by the reloc.c:init_pltgot() routine.
+ * The first time an external routine is called, the PLT slot will
+ * set up %r11 to the offset of the slot, and will jump to this routine.
+ * The ELF object is shifted into %r11, and _rtld_bind_start is called
+ * to complete the binding.
+ */
+_ENTRY(_rtld_powerpc_pltlongresolve)
+ lis %r12,0 # lis 12,jmptab@ha
+ addi %r12,%r12,0 # addi 12,12,jmptab@l
+ subf %r11,%r12,%r11 # reloff
+ li %r12,2
+ srw %r11,%r11,%r12 # index = reloff/sizeof(Elf_Addr)
+_END(_rtld_powerpc_pltlongresolve)
+_ENTRY(_rtld_powerpc_pltresolve)
+ lis %r12,0 # lis 12,_rtld_bind_start@ha
+ addi %r12,%r12,0 # addi 12,12,_rtld_bind_start@l
+ mtctr %r12
+ lis %r12,0 # lis 12,obj@ha
+ addi %r12,%r12,0 # addi 12,12,obj@l
+ bctr
+_END(_rtld_powerpc_pltresolve)
+
+/*
+ * _rtld_powerpc_pltcall()
+ *
+ * This routine is copied into the 72-byte reserved area at the
+ * start of the PLT. The reloc.c:init_pltgot() routine inserts
+ * the absolute address of the jumptable.
+ * Control is transferred to this routine when the binder has
+ * located the external routine, but determined that it is > 32Mb
+ * from the PLT slot. Code is inserted into the PLT slot to set up
+ * %r11 with the jumptable index, and jump to here, where the
+ * absolute address of the external routine is loaded from the
+ * jumptable and transferred to
+ */
+_ENTRY(_rtld_powerpc_pltcall)
+ slwi %r11,%r11,2 # jmptab offset = index * 4
+ addis %r11,%r11,0 # addis 11,11,jmptab@ha
+ lwz %r11,0(%r11) # lwz 11,jmptab@l(11)
+ mtctr %r11
+ bctr # (*jmptab[index])()
+_END(_rtld_powerpc_pltcall)
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/powerpc64/Makefile.inc b/libexec/rtld-elf/powerpc64/Makefile.inc
new file mode 100644
index 000000000000..641336724894
--- /dev/null
+++ b/libexec/rtld-elf/powerpc64/Makefile.inc
@@ -0,0 +1 @@
+RTLD_ENTRY= _rtld_start
diff --git a/libexec/rtld-elf/powerpc64/reloc.c b/libexec/rtld-elf/powerpc64/reloc.c
new file mode 100644
index 000000000000..9ea14f63b5c7
--- /dev/null
+++ b/libexec/rtld-elf/powerpc64/reloc.c
@@ -0,0 +1,739 @@
+/* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 1998 Tsubai Masanari
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/sysctl.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/cpu.h>
+#include <machine/md_var.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+#if !defined(_CALL_ELF) || _CALL_ELF == 1
+struct funcdesc {
+ Elf_Addr addr;
+ Elf_Addr toc;
+ Elf_Addr env;
+};
+#endif
+
+bool
+arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp)
+{
+ if (dynp->d_tag == DT_PPC64_GLINK) {
+ obj->glink = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr);
+ return (true);
+ }
+
+ return (false);
+}
+
+/*
+ * Process the R_PPC_COPY relocations
+ */
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ /*
+ * COPY relocs are invalid outside of the main program
+ */
+ assert(dstobj->mainprog);
+
+ relalim = (const Elf_Rela *)((const char *) dstobj->rela +
+ dstobj->relasize);
+ for (rela = dstobj->rela; rela < relalim; rela++) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym = NULL;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) {
+ continue;
+ }
+
+ dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = globallist_next(dstobj); srcobj != NULL;
+ srcobj = globallist_next(srcobj)) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+
+ if (srcobj == NULL) {
+ _rtld_error("Undefined symbol \"%s\" "
+ " referenced from COPY"
+ " relocation in %s", name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase+srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ dbg("copy_reloc: src=%p,dst=%p,size=%zd\n",srcaddr,dstaddr,size);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Perform early relocation of the run-time linker image
+ */
+void
+reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
+{
+ const Elf_Rela *rela = NULL, *relalim;
+ Elf_Addr relasz = 0;
+ Elf_Addr *where;
+
+ /*
+ * Extract the rela/relasz values from the dynamic section
+ */
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_RELA:
+ rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ relasz = dynp->d_un.d_val;
+ break;
+ }
+ }
+
+ /*
+ * Relocate these values
+ */
+ relalim = (const Elf_Rela *)((const char *)rela + relasz);
+ for (; rela < relalim; rela++) {
+ where = (Elf_Addr *)(relocbase + rela->r_offset);
+ *where = (Elf_Addr)(relocbase + rela->r_addend);
+ }
+}
+
+
+/*
+ * Relocate a non-PLT object with addend.
+ */
+static int
+reloc_nonplt_object(Obj_Entry *obj_rtld __unused, Obj_Entry *obj,
+ const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate)
+{
+ const Elf_Sym *def = NULL;
+ const Obj_Entry *defobj;
+ Elf_Addr *where, symval = 0;
+
+ /*
+ * First, resolve symbol for relocations which
+ * reference symbols.
+ */
+ switch (ELF_R_TYPE(rela->r_info)) {
+
+ case R_PPC64_UADDR64: /* doubleword64 S + A */
+ case R_PPC64_ADDR64:
+ case R_PPC_GLOB_DAT:
+ case R_PPC64_DTPMOD64:
+ case R_PPC64_TPREL64:
+ case R_PPC64_DTPREL64:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+ if (def == NULL) {
+ return (-1);
+ }
+ /*
+ * If symbol is IFUNC, only perform relocation
+ * when caller allowed it by passing
+ * SYMLOOK_IFUNC flag. Skip the relocations
+ * otherwise.
+ *
+ * Also error out in case IFUNC relocations
+ * are specified for TLS, which cannot be
+ * usefully interpreted.
+ */
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_PPC64_UADDR64:
+ case R_PPC64_ADDR64:
+ case R_PPC_GLOB_DAT:
+ if ((flags & SYMLOOK_IFUNC) == 0) {
+ dbg("Non-PLT reference to IFUNC found!");
+ obj->non_plt_gnu_ifunc = true;
+ return (0);
+ }
+ symval = (Elf_Addr)rtld_resolve_ifunc(
+ defobj, def);
+ break;
+ default:
+ _rtld_error("%s: IFUNC for TLS reloc",
+ obj->path);
+ return (-1);
+ }
+ } else {
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ return (0);
+ symval = (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ }
+ break;
+ default:
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ return (0);
+ }
+
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_PPC_NONE:
+ break;
+ case R_PPC64_UADDR64:
+ case R_PPC64_ADDR64:
+ case R_PPC_GLOB_DAT:
+ /* Don't issue write if unnecessary; avoid COW page fault */
+ if (*where != symval + rela->r_addend) {
+ *where = symval + rela->r_addend;
+ }
+ break;
+ case R_PPC64_DTPMOD64:
+ *where = (Elf_Addr) defobj->tlsindex;
+ break;
+ case R_PPC64_TPREL64:
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_static) {
+ if (!allocate_tls_offset(
+ __DECONST(Obj_Entry *, defobj))) {
+ _rtld_error("%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ return (-1);
+ }
+ }
+
+ *(Elf_Addr **)where = *where * sizeof(Elf_Addr)
+ + (Elf_Addr *)(def->st_value + rela->r_addend
+ + defobj->tlsoffset - TLS_TP_OFFSET - TLS_TCB_SIZE);
+ break;
+ case R_PPC64_DTPREL64:
+ *where += (Elf_Addr)(def->st_value + rela->r_addend
+ - TLS_DTV_OFFSET);
+ break;
+ case R_PPC_RELATIVE: /* doubleword64 B + A */
+ symval = (Elf_Addr)(obj->relocbase + rela->r_addend);
+
+ /* As above, don't issue write unnecessarily */
+ if (*where != symval) {
+ *where = symval;
+ }
+ break;
+ case R_PPC_COPY:
+ /*
+ * These are deferred until all other relocations
+ * have been done. All we do here is make sure
+ * that the COPY relocation is not in a shared
+ * library. They are allowed only in executable
+ * files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_COPY "
+ " relocation in shared library",
+ obj->path);
+ return (-1);
+ }
+ break;
+ case R_PPC_IRELATIVE:
+ /*
+ * These will be handled by reloc_iresolve().
+ */
+ obj->irelative = true;
+ break;
+ case R_PPC_JMP_SLOT:
+ /*
+ * These will be handled by the plt/jmpslot routines
+ */
+ break;
+
+ default:
+ _rtld_error("%s: Unsupported relocation type %ld"
+ " in non-PLT relocations\n", obj->path,
+ ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ return (0);
+}
+
+
+/*
+ * Process non-PLT relocations
+ */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Phdr *phdr;
+ SymCache *cache;
+ int bytes = obj->dynsymcount * sizeof(SymCache);
+ int r = -1;
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj != obj_rtld) {
+ cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON,
+ -1, 0);
+ if (cache == MAP_FAILED)
+ cache = NULL;
+ } else
+ cache = NULL;
+
+ /*
+ * From the SVR4 PPC ABI:
+ * "The PowerPC family uses only the Elf32_Rela relocation
+ * entries with explicit addends."
+ */
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags,
+ lockstate) < 0)
+ goto done;
+ }
+ r = 0;
+done:
+ if (cache)
+ munmap(cache, bytes);
+
+ /*
+ * Synchronize icache for executable segments in case we made
+ * any changes.
+ */
+ for (phdr = obj->phdr;
+ (const char *)phdr < (const char *)obj->phdr + obj->phsize;
+ phdr++) {
+ if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X) != 0) {
+ __syncicache(obj->relocbase + phdr->p_vaddr,
+ phdr->p_memsz);
+ }
+ }
+
+ return (r);
+}
+
+
+/*
+ * Initialise a PLT slot to the resolving trampoline
+ */
+static int
+reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela)
+{
+ Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ long reloff;
+
+ reloff = rela - obj->pltrela;
+
+ dbg(" reloc_plt_object: where=%p,reloff=%lx,glink=%#lx", (void *)where,
+ reloff, obj->glink);
+
+#if !defined(_CALL_ELF) || _CALL_ELF == 1
+ /* Glink code is 3 instructions after the first 32k, 2 before */
+ *where = (Elf_Addr)obj->glink + 32 +
+ 8*((reloff < 0x8000) ? reloff : 0x8000) +
+ 12*((reloff < 0x8000) ? 0 : (reloff - 0x8000));
+#else
+ /* 64-Bit ELF V2 ABI Specification, sec. 4.2.5.3. */
+ *where = (Elf_Addr)obj->glink + 4*reloff + 32;
+#endif
+
+ return (0);
+}
+
+/*
+ * Process the PLT relocations.
+ */
+int
+reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (obj->pltrelasize != 0) {
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {
+ dbg("ABI violation - found IRELATIVE in the PLT.");
+ obj->irelative = true;
+ continue;
+ }
+#endif
+ /*
+ * PowerPC(64) .rela.plt is composed of an array of
+ * R_PPC_JMP_SLOT relocations. Unlike other platforms,
+ * this is the ONLY relocation type that is valid here.
+ */
+ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
+
+ if (reloc_plt_object(obj, rela) < 0) {
+ return (-1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * LD_BIND_NOW was set - force relocation for all jump slots
+ */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+ Elf_Addr *where;
+ Elf_Addr target;
+
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ /* This isn't actually a jump slot, ignore it. */
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE)
+ continue;
+ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL) {
+ dbg("reloc_jmpslots: sym not found");
+ return (-1);
+ }
+
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+
+ if (def == &sym_zero) {
+ /* Zero undefined weak symbols */
+#if !defined(_CALL_ELF) || _CALL_ELF == 1
+ bzero(where, sizeof(struct funcdesc));
+#else
+ *where = 0;
+#endif
+ } else {
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ /* LD_BIND_NOW, ifunc in shared lib.*/
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *) rela);
+ }
+ }
+
+ obj->jmpslots_done = true;
+
+ return (0);
+}
+
+
+/*
+ * Update the value of a PLT jump slot.
+ */
+Elf_Addr
+reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj __unused,
+ const Obj_Entry *obj __unused, const Elf_Rel *rel __unused)
+{
+
+ /*
+ * At the PLT entry pointed at by `wherep', construct
+ * a direct transfer to the now fully resolved function
+ * address.
+ */
+
+#if !defined(_CALL_ELF) || _CALL_ELF == 1
+ dbg(" reloc_jmpslot: where=%p, target=%p (%#lx + %#lx)",
+ (void *)wherep, (void *)target, *(Elf_Addr *)target,
+ (Elf_Addr)defobj->relocbase);
+
+ if (ld_bind_not)
+ goto out;
+
+ /*
+ * For the trampoline, the second two elements of the function
+ * descriptor are unused, so we are fine replacing those at any time
+ * with the real ones with no thread safety implications. However, we
+ * need to make sure the main entry point pointer ([0]) is seen to be
+ * modified *after* the second two elements. This can't be done in
+ * general, since there are no barriers in the reading code, but put in
+ * some isyncs to at least make it a little better.
+ */
+ memcpy(wherep, (void *)target, sizeof(struct funcdesc));
+ wherep[2] = ((Elf_Addr *)target)[2];
+ wherep[1] = ((Elf_Addr *)target)[1];
+ __asm __volatile ("isync" : : : "memory");
+ wherep[0] = ((Elf_Addr *)target)[0];
+ __asm __volatile ("isync" : : : "memory");
+
+ if (((struct funcdesc *)(wherep))->addr < (Elf_Addr)defobj->relocbase) {
+ /*
+ * It is possible (LD_BIND_NOW) that the function
+ * descriptor we are copying has not yet been relocated.
+ * If this happens, fix it. Don't worry about threading in
+ * this case since LD_BIND_NOW makes it irrelevant.
+ */
+
+ ((struct funcdesc *)(wherep))->addr +=
+ (Elf_Addr)defobj->relocbase;
+ ((struct funcdesc *)(wherep))->toc +=
+ (Elf_Addr)defobj->relocbase;
+ }
+#else
+ dbg(" reloc_jmpslot: where=%p, target=%p", (void *)wherep,
+ (void *)target);
+
+ assert(target >= (Elf_Addr)defobj->relocbase);
+
+ if (ld_bind_not)
+ goto out;
+
+ if (*wherep != target)
+ *wherep = target;
+
+#endif
+out:
+
+ return (target);
+}
+
+int
+reloc_iresolve(Obj_Entry *obj,
+ struct Struct_RtldLockState *lockstate)
+{
+ /*
+ * Since PLT slots on PowerPC64 are always R_PPC_JMP_SLOT,
+ * R_PPC_IRELATIVE is in RELA.
+ */
+#if !defined(_CALL_ELF) || _CALL_ELF == 1
+ (void)(obj);
+ (void)(lockstate);
+ /* XXX not implemented */
+ return (0);
+#else
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ Elf_Addr *where, target, *ptr;
+
+ if (!obj->irelative)
+ return (0);
+
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {
+ ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+
+ lock_release(rtld_bind_lock, lockstate);
+ target = call_ifunc_resolver(ptr);
+ wlock_acquire(rtld_bind_lock, lockstate);
+
+ *where = target;
+ }
+ }
+ /*
+ * XXX Remove me when lld is fixed!
+ * LLD currently makes illegal relocations in the PLT.
+ */
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {
+ ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+
+ lock_release(rtld_bind_lock, lockstate);
+ target = call_ifunc_resolver(ptr);
+ wlock_acquire(rtld_bind_lock, lockstate);
+
+ *where = target;
+ }
+ }
+
+ obj->irelative = false;
+ return (0);
+#endif
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused,
+ struct Struct_RtldLockState *lockstate __unused)
+{
+#if !defined(_CALL_ELF) || _CALL_ELF == 1
+ _rtld_error("reloc_gnu_ifunc(): Not implemented!");
+ /* XXX not implemented */
+ return (-1);
+#else
+
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT) {
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+ lock_release(rtld_bind_lock, lockstate);
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *)rela);
+ }
+ }
+ obj->gnu_ifunc = false;
+ return (0);
+#endif
+}
+
+int
+reloc_iresolve_nonplt(Obj_Entry *obj __unused,
+ struct Struct_RtldLockState *lockstate __unused)
+{
+ return (0);
+}
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+ Elf_Addr *pltcall;
+
+ pltcall = obj->pltgot;
+
+ if (pltcall == NULL) {
+ return;
+ }
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+ pltcall[0] = (Elf_Addr)&_rtld_bind_start;
+ pltcall[1] = (Elf_Addr)obj;
+#else
+ memcpy(pltcall, _rtld_bind_start, sizeof(struct funcdesc));
+ pltcall[2] = (Elf_Addr)obj;
+#endif
+}
+
+/*
+ * Actual values are 32 bit.
+ */
+u_long cpu_features;
+u_long cpu_features2;
+
+void
+powerpc64_abi_variant_hook(Elf_Auxinfo** aux_info)
+{
+ /*
+ * Since aux_info[] is easier to work with than aux, go ahead and
+ * initialize cpu_features / cpu_features2.
+ */
+ cpu_features = -1UL;
+ cpu_features2 = -1UL;
+ if (aux_info[AT_HWCAP] != NULL)
+ cpu_features = (uint32_t)aux_info[AT_HWCAP]->a_un.a_val;
+ if (aux_info[AT_HWCAP2] != NULL)
+ cpu_features2 = (uint32_t)aux_info[AT_HWCAP2]->a_un.a_val;
+}
+
+void
+ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)
+{
+
+}
+
+void
+allocate_initial_tls(Obj_Entry *list)
+{
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+
+ tls_static_space = tls_last_offset + tls_last_size +
+ ld_static_tls_extra;
+
+ _tcb_set(allocate_tls(list, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN));
+}
+
+void*
+__tls_get_addr(tls_index* ti)
+{
+ return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset +
+ TLS_DTV_OFFSET));
+}
diff --git a/libexec/rtld-elf/powerpc64/rtld_machdep.h b/libexec/rtld-elf/powerpc64/rtld_machdep.h
new file mode 100644
index 000000000000..d628e776bae9
--- /dev/null
+++ b/libexec/rtld-elf/powerpc64/rtld_machdep.h
@@ -0,0 +1,93 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+#include <machine/tls.h>
+
+struct Struct_Obj_Entry;
+
+#define MD_OBJ_ENTRY \
+ Elf_Addr glink; /* GLINK PLT call stub section */
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) (&_DYNAMIC)
+
+bool arch_digest_dynamic(struct Struct_Obj_Entry *, const Elf_Dyn *);
+
+/* No architecture specific notes */
+#define arch_digest_note(obj, note) false
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj,
+ const Elf_Rel *rel);
+void reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+extern u_long cpu_features; /* r3 */
+extern u_long cpu_features2; /* r4 */
+/* r5-r10: ifunc resolver parameters reserved for future assignment. */
+#define call_ifunc_resolver(ptr) \
+ (((Elf_Addr (*)(uint32_t, uint32_t, uint64_t, uint64_t, uint64_t, \
+ uint64_t, uint64_t, uint64_t))ptr)((uint32_t)cpu_features, \
+ (uint32_t)cpu_features2, 0, 0, 0, 0, 0, 0))
+
+/*
+ * TLS
+ */
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align, offset) \
+ TLS_TCB_SIZE
+#define calculate_tls_offset(prev_offset, prev_size, size, align, offset) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_post_size(align) 0
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index* ti);
+
+extern void powerpc64_abi_variant_hook(Elf_Auxinfo **);
+#define md_abi_variant_hook(x) powerpc64_abi_variant_hook(x)
+
+#endif
diff --git a/libexec/rtld-elf/powerpc64/rtld_start.S b/libexec/rtld-elf/powerpc64/rtld_start.S
new file mode 100644
index 000000000000..796a41e34601
--- /dev/null
+++ b/libexec/rtld-elf/powerpc64/rtld_start.S
@@ -0,0 +1,179 @@
+/* $NetBSD: rtld_start.S,v 1.4 2001/09/26 04:06:43 mycroft Exp $ */
+
+/*-
+ * Copyright (C) 1998 Tsubai Masanari
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+
+.extern _GLOBAL_OFFSET_TABLE_
+.extern _DYNAMIC
+
+_ENTRY(_rtld_start)
+ stdu %r1,-144(%r1) /* 16-byte aligned stack for reg saves +
+ exit_proc & obj _rtld args +
+ backchain & lrsave stack frame */
+
+ /* Save and restore only initial argv, because _rtld will modify
+ * argv and envp if invoked explicitly, making it necessary to
+ * load the (possibly) adjusted values from the stack.
+ */
+ std %r4,104(%r1) /* argv */
+/* std %r6,120(%r1) *//* obj (always 0) */
+/* std %r7,128(%r1) *//* cleanup (always 0) */
+ std %r8,136(%r1) /* ps_strings */
+
+ /*
+ * Perform initial relocation of ld-elf.so. Not as easy as it
+ * sounds.
+ * - perform small forward branch to put PC into link reg
+ * - use link-time constants to determine offset to the
+ * _DYNAMIC section and the GOT. Add these to the PC to
+ * convert to absolute addresses.
+ * - call reloc_non_plt_self() to fix up ld-elf.so's relocations
+ */
+
+ bl 1f
+ .llong _DYNAMIC-.
+1:
+ mflr %r3 /* PC value at .llong */
+ ld %r4,0(%r3) /* offset to _DYNAMIC */
+ add %r3,%r4,%r3 /* r3 = &_DYNAMIC, absolute value */
+
+ ld %r4,-0x8000(%r2) /* First TOC entry is TOC base */
+ subf %r4,%r4,%r2 /* Subtract from real TOC base to get base */
+
+ bl reloc_non_plt_self /* reloc_non_plt_self(&_DYNAMIC,base) */
+ nop
+
+ /*
+ * The _rtld() function likes to see a stack layout containing
+ * { argc, argv[0], argv[1] ... argv[N], 0, env[0], ... , env[N] }
+ * Since the PowerPC stack was 16-byte aligned at exec time, the
+ * original stack layout has to be found by moving back a word
+ * from the argv pointer.
+ */
+ ld %r4,104(%r1)
+ addi %r3,%r4,-8 /* locate argc ptr, &argv[-1] */
+ addi %r4,%r1,128 /* &exit_proc on stack */
+ addi %r5,%r1,120 /* &obj_main on stack */
+
+ bl _rtld /* &_start = _rtld(sp, &exit_proc, &obj_main)*/
+ nop
+#if !defined(_CALL_ELF) || _CALL_ELF == 1
+ ld %r2,8(%r3)
+ ld %r11,16(%r3)
+ ld %r3,0(%r3)
+#else
+ mr %r12,%r3
+#endif
+ mtlr %r3
+
+ /*
+ * Restore args, with new obj/exit proc
+ */
+ ld %r4,104(%r1) /* argv */
+ ld %r3,-8(%r4) /* argc */
+
+ /* envp = argv + argc + 1 */
+ addi %r5,%r3,1
+ sldi %r5,%r5,3 /* x8 */
+ add %r5,%r4,%r5
+
+ ld %r6,120(%r1) /* obj */
+ ld %r7,128(%r1) /* exit proc */
+ ld %r8,136(%r1) /* ps_strings */
+
+ blrl /* _start(argc, argv, envp, obj, cleanup, ps_strings) */
+
+ li %r0,1 /* _exit() */
+ sc
+_END(_rtld_start)
+
+/*
+ * _rtld_bind_start()
+ *
+ * Call into the MI binder. This routine is reached via the PLT call cell
+ *
+ * On entry, %r11 contains an object pointer and %r0 contains the PLT index.
+ *
+ * Save all registers, call into the binder to resolve and fixup the external
+ * routine, and then transfer to the external routine on return.
+ */
+ .globl _rtld_bind
+
+_ENTRY(_rtld_bind_start)
+ mr %r12,%r0 # save r0 (index) immediately to r12
+ mflr %r0
+ std %r0,16(%r1) # save lr
+ mfcr %r0
+ std %r0,8(%r1) # save cr
+
+ stdu %r1,-48-12*8(%r1) # stack space for 8 regs + header
+ # + 2 save regs
+ std %r3,64+0*8(%r1) # save r3-r10 (arguments)
+ std %r4,64+1*8(%r1)
+ std %r5,64+2*8(%r1)
+ std %r6,64+3*8(%r1)
+ std %r7,64+4*8(%r1)
+ std %r8,64+5*8(%r1)
+ std %r9,64+6*8(%r1)
+ std %r10,64+7*8(%r1)
+
+ mr %r3,%r11
+ mulli %r4,%r12,24 # Multiply index by sizeof(Elf_Rela)
+
+ bl _rtld_bind # target addr = _rtld_bind(obj, reloff)
+ nop
+
+#if !defined(_CALL_ELF) || _CALL_ELF == 1
+ ld %r2,8(%r3)
+ ld %r11,16(%r3)
+ ld %r3,0(%r3)
+#else
+ mr %r12,%r3
+#endif
+ mtctr %r3 # move absolute target addr into ctr
+
+ ld %r3,64+0*8(%r1) # restore r3-r10
+ ld %r4,64+1*8(%r1)
+ ld %r5,64+2*8(%r1)
+ ld %r6,64+3*8(%r1)
+ ld %r7,64+4*8(%r1)
+ ld %r8,64+5*8(%r1)
+ ld %r9,64+6*8(%r1)
+ ld %r10,64+7*8(%r1)
+
+ ld %r1,0(%r1) # restore stack
+ ld %r0,8(%r1) # restore cr
+ mtcr %r0
+ ld %r0,16(%r1) # restore lr
+ mtlr %r0
+
+ bctr # jump to target
+_END(_rtld_bind_start)
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/riscv/reloc.c b/libexec/rtld-elf/riscv/reloc.c
new file mode 100644
index 000000000000..25c0befb774e
--- /dev/null
+++ b/libexec/rtld-elf/riscv/reloc.c
@@ -0,0 +1,476 @@
+/*-
+ * Copyright (c) 2015-2017 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * This software was developed by the University of Cambridge Computer
+ * Laboratory as part of the CTSRD Project, with support from the UK Higher
+ * Education Innovation Fund (HEIF).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "rtld_printf.h"
+
+uint64_t
+set_gp(Obj_Entry *obj)
+{
+ uint64_t old;
+ SymLook req;
+ uint64_t gp;
+ int res;
+
+ __asm __volatile("mv %0, gp" : "=r"(old));
+
+ symlook_init(&req, "__global_pointer$");
+ req.ventry = NULL;
+ req.flags = SYMLOOK_EARLY;
+ res = symlook_obj(&req, obj);
+
+ if (res == 0) {
+ gp = req.sym_out->st_value;
+ __asm __volatile("mv gp, %0" :: "r"(gp));
+ }
+
+ return (old);
+}
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+
+ if (obj->pltgot != NULL) {
+ obj->pltgot[0] = (Elf_Addr)&_rtld_bind_start;
+ obj->pltgot[1] = (Elf_Addr)obj;
+ }
+}
+
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Obj_Entry *srcobj, *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *srcsym;
+ const Elf_Sym *dstsym;
+ const void *srcaddr;
+ const char *name;
+ void *dstaddr;
+ SymLook req;
+ size_t size;
+ int res;
+
+ /*
+ * COPY relocs are invalid outside of the main program
+ */
+ assert(dstobj->mainprog);
+
+ relalim = (const Elf_Rela *)((const char *)dstobj->rela +
+ dstobj->relasize);
+ for (rela = dstobj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) != R_RISCV_COPY)
+ continue;
+
+ dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = globallist_next(dstobj); srcobj != NULL;
+ srcobj = globallist_next(srcobj)) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+ if (srcobj == NULL) {
+ _rtld_error(
+"Undefined symbol \"%s\" referenced from COPY relocation in %s",
+ name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase + srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+
+ return (0);
+}
+
+/*
+ * Process the PLT relocations.
+ */
+int
+reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where;
+
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_RISCV_JUMP_SLOT:
+ *where += (Elf_Addr)obj->relocbase;
+ break;
+ case R_RISCV_IRELATIVE:
+ obj->irelative = true;
+ break;
+ default:
+ _rtld_error("Unknown relocation type %u in PLT",
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * LD_BIND_NOW was set - force relocation for all jump slots
+ */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where;
+
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ switch(ELF_R_TYPE(rela->r_info)) {
+ case R_RISCV_JUMP_SLOT:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj,
+ &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL) {
+ dbg("reloc_jmpslots: sym not found");
+ return (-1);
+ }
+
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ obj->gnu_ifunc = true;
+ continue;
+ }
+
+ *where = (Elf_Addr)(defobj->relocbase + def->st_value);
+ break;
+ default:
+ _rtld_error("Unknown relocation type %x in jmpslot",
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela,
+ RtldLockState *lockstate)
+{
+ Elf_Addr *where, target, *ptr;
+
+ ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ lock_release(rtld_bind_lock, lockstate);
+ target = call_ifunc_resolver(ptr);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ *where = target;
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->irelative)
+ return (0);
+
+ obj->irelative = false;
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_RISCV_IRELATIVE)
+ reloc_iresolve_one(obj, rela, lockstate);
+ }
+ return (0);
+}
+
+int
+reloc_iresolve_nonplt(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->irelative_nonplt)
+ return (0);
+
+ obj->irelative_nonplt = false;
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_RISCV_IRELATIVE)
+ reloc_iresolve_one(obj, rela, lockstate);
+ }
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags,
+ struct Struct_RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+
+ relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_RISCV_JUMP_SLOT) {
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+
+ lock_release(rtld_bind_lock, lockstate);
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *)rela);
+ }
+ }
+ obj->gnu_ifunc = false;
+ return (0);
+}
+
+Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const Obj_Entry *defobj __unused, const Obj_Entry *obj __unused,
+ const Elf_Rel *rel)
+{
+
+ assert(ELF_R_TYPE(rel->r_info) == R_RISCV_JUMP_SLOT ||
+ ELF_R_TYPE(rel->r_info) == R_RISCV_IRELATIVE);
+
+ if (*where != target && !ld_bind_not)
+ *where = target;
+ return (target);
+}
+
+/*
+ * Process non-PLT relocations
+ */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+ SymCache *cache;
+ Elf_Addr *where, symval;
+ unsigned long symnum;
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj == obj_rtld)
+ cache = NULL;
+ else
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+
+ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ symnum = ELF_R_SYM(rela->r_info);
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_RISCV_JUMP_SLOT:
+ /* This will be handled by the plt/jmpslot routines */
+ break;
+ case R_RISCV_NONE:
+ break;
+ case R_RISCV_64:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return (-1);
+
+ /*
+ * If symbol is IFUNC, only perform relocation
+ * when caller allowed it by passing
+ * SYMLOOK_IFUNC flag. Skip the relocations
+ * otherwise.
+ */
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ if ((flags & SYMLOOK_IFUNC) == 0) {
+ obj->non_plt_gnu_ifunc = true;
+ continue;
+ }
+ symval = (Elf_Addr)rtld_resolve_ifunc(defobj,
+ def);
+ } else {
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ symval = (Elf_Addr)(defobj->relocbase +
+ def->st_value);
+ }
+
+ *where = symval + rela->r_addend;
+ break;
+ case R_RISCV_TLS_DTPMOD64:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+
+ *where += (Elf_Addr)defobj->tlsindex;
+ break;
+ case R_RISCV_COPY:
+ /*
+ * These are deferred until all other relocations have
+ * been done. All we do here is make sure that the
+ * COPY relocation is not in a shared library. They
+ * are allowed only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_RISCV_COPY "
+ "relocation in shared library", obj->path);
+ return (-1);
+ }
+ break;
+ case R_RISCV_TLS_DTPREL64:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return (-1);
+
+ *where += (Elf_Addr)(def->st_value + rela->r_addend
+ - TLS_DTV_OFFSET);
+ break;
+ case R_RISCV_TLS_TPREL64:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return (-1);
+
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_static) {
+ if (!allocate_tls_offset(
+ __DECONST(Obj_Entry *, defobj))) {
+ _rtld_error(
+ "%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ return (-1);
+ }
+ }
+
+ *where = (def->st_value + rela->r_addend +
+ defobj->tlsoffset - TLS_TP_OFFSET - TLS_TCB_SIZE);
+ break;
+ case R_RISCV_RELATIVE:
+ *where = (Elf_Addr)(obj->relocbase + rela->r_addend);
+ break;
+ case R_RISCV_IRELATIVE:
+ obj->irelative_nonplt = true;
+ break;
+ default:
+ rtld_printf("%s: Unhandled relocation %lu\n",
+ obj->path, ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+unsigned long elf_hwcap;
+
+void
+ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)])
+{
+ if (aux_info[AT_HWCAP] != NULL)
+ elf_hwcap = aux_info[AT_HWCAP]->a_un.a_val;
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + tls_last_size +
+ ld_static_tls_extra;
+
+ _tcb_set(allocate_tls(objs, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN));
+}
+
+void *
+__tls_get_addr(tls_index* ti)
+{
+ return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset +
+ TLS_DTV_OFFSET));
+}
diff --git a/libexec/rtld-elf/riscv/rtld_machdep.h b/libexec/rtld-elf/riscv/rtld_machdep.h
new file mode 100644
index 000000000000..c6600b583612
--- /dev/null
+++ b/libexec/rtld-elf/riscv/rtld_machdep.h
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Portions of this software were developed by SRI International and the
+ * University of Cambridge Computer Laboratory under DARPA/AFRL contract
+ * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Portions of this software were developed by the University of Cambridge
+ * Computer Laboratory as part of the CTSRD Project, with support from the
+ * UK Higher Education Innovation Fund (HEIF).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+#include <machine/tls.h>
+
+struct Struct_Obj_Entry;
+
+#define MD_OBJ_ENTRY
+
+uint64_t set_gp(struct Struct_Obj_Entry *obj);
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) \
+({ \
+ Elf_Addr _dynamic_addr; \
+ __asm __volatile("lla %0, _DYNAMIC" : "=r"(_dynamic_addr)); \
+ (const Elf_Dyn *)_dynamic_addr; \
+})
+
+/* No arch-specific dynamic tags */
+#define arch_digest_dynamic(obj, dynp) false
+
+/* No architecture specific notes */
+#define arch_digest_note(obj, note) false
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj,
+ const Elf_Rel *rel);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+({ \
+ uint64_t old0; \
+ old0 = set_gp(obj); \
+ (((InitFunc)(target))()); \
+ __asm __volatile("mv gp, %0" :: "r"(old0)); \
+})
+
+#define call_init_pointer(obj, target) \
+({ \
+ uint64_t old1; \
+ old1 = set_gp(obj); \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ)); \
+ __asm __volatile("mv gp, %0" :: "r"(old1)); \
+})
+
+extern unsigned long elf_hwcap;
+#define call_ifunc_resolver(ptr) \
+ (((Elf_Addr (*)(unsigned long, unsigned long, unsigned long, \
+ unsigned long, unsigned long, unsigned long, unsigned long, \
+ unsigned long))ptr)(elf_hwcap, 0, 0, 0, 0, 0, 0, 0))
+
+/*
+ * TLS
+ */
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align, offset) \
+ TLS_TCB_SIZE
+#define calculate_tls_offset(prev_offset, prev_size, size, align, offset) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_post_size(align) 0
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index* ti);
+
+#define md_abi_variant_hook(x)
+
+#endif
diff --git a/libexec/rtld-elf/riscv/rtld_start.S b/libexec/rtld-elf/riscv/rtld_start.S
new file mode 100644
index 000000000000..e0b8157c964f
--- /dev/null
+++ b/libexec/rtld-elf/riscv/rtld_start.S
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 2015-2018 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * This software was developed by the University of Cambridge Computer
+ * Laboratory as part of the CTSRD Project, with support from the UK Higher
+ * Education Innovation Fund (HEIF).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+/*
+ * func_ptr_type
+ * _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
+ */
+
+ENTRY(.rtld_start)
+ .cfi_undefined ra /* Do not attempt to unwind any further. */
+ mv s0, a0 /* Put ps_strings in a callee-saved register */
+ mv s1, sp /* And the stack pointer */
+
+ addi sp, sp, -16 /* Make room for obj_main & exit proc */
+
+ mv a1, sp /* exit_proc */
+ addi a2, a1, 8 /* obj_main */
+ jal _rtld /* Call the loader */
+ mv t0, a0 /* Backup the entry point */
+
+ ld a2, 0(sp) /* Load cleanup */
+ ld a1, 8(sp) /* Load obj_main */
+ mv a0, s0 /* Restore ps_strings */
+ mv sp, s1 /* Restore the stack pointer */
+ jr t0 /* Jump to the entry point */
+END(.rtld_start)
+
+/*
+ * t0 = obj pointer
+ * t1 = reloc offset
+ */
+ENTRY(_rtld_bind_start)
+ /* Save the arguments and ra */
+ /* We require 17 dwords, but the stack must be aligned to 16-bytes */
+ addi sp, sp, -(8 * 18)
+ sd a0, (8 * 0)(sp)
+ sd a1, (8 * 1)(sp)
+ sd a2, (8 * 2)(sp)
+ sd a3, (8 * 3)(sp)
+ sd a4, (8 * 4)(sp)
+ sd a5, (8 * 5)(sp)
+ sd a6, (8 * 6)(sp)
+ sd a7, (8 * 7)(sp)
+ sd ra, (8 * 8)(sp)
+
+#ifdef __riscv_float_abi_double
+ /* Save any floating-point arguments */
+ fsd fa0, (8 * 9)(sp)
+ fsd fa1, (8 * 10)(sp)
+ fsd fa2, (8 * 11)(sp)
+ fsd fa3, (8 * 12)(sp)
+ fsd fa4, (8 * 13)(sp)
+ fsd fa5, (8 * 14)(sp)
+ fsd fa6, (8 * 15)(sp)
+ fsd fa7, (8 * 16)(sp)
+#endif
+
+ /* Reloc offset is 3x of the .got.plt offset */
+ slli a1, t1, 1 /* Mult items by 2 */
+ add a1, a1, t1 /* Plus item */
+
+ /* Load obj */
+ mv a0, t0
+
+ /* Call into rtld */
+ jal _rtld_bind
+
+ /* Backup the address to branch to */
+ mv t0, a0
+
+ /* Restore the arguments and ra */
+ ld a0, (8 * 0)(sp)
+ ld a1, (8 * 1)(sp)
+ ld a2, (8 * 2)(sp)
+ ld a3, (8 * 3)(sp)
+ ld a4, (8 * 4)(sp)
+ ld a5, (8 * 5)(sp)
+ ld a6, (8 * 6)(sp)
+ ld a7, (8 * 7)(sp)
+ ld ra, (8 * 8)(sp)
+
+#ifdef __riscv_float_abi_double
+ /* Restore floating-point arguments */
+ fld fa0, (8 * 9)(sp)
+ fld fa1, (8 * 10)(sp)
+ fld fa2, (8 * 11)(sp)
+ fld fa3, (8 * 12)(sp)
+ fld fa4, (8 * 13)(sp)
+ fld fa5, (8 * 14)(sp)
+ fld fa6, (8 * 15)(sp)
+ fld fa7, (8 * 16)(sp)
+#endif
+ addi sp, sp, (8 * 18)
+
+ /* Call into the correct function */
+ jr t0
+END(_rtld_bind_start)
diff --git a/libexec/rtld-elf/rtld-libc/Makefile.inc b/libexec/rtld-elf/rtld-libc/Makefile.inc
new file mode 100644
index 000000000000..a10bd562a7ce
--- /dev/null
+++ b/libexec/rtld-elf/rtld-libc/Makefile.inc
@@ -0,0 +1,110 @@
+# This makefiles adds the necessary libc dependencies for RTLD without pulling
+# in all of the complex libc bits such as locales, etc.
+
+.include <bsd.compiler.mk>
+
+LIBC_SRCTOP=${SRCTOP}/lib/libc
+.if exists(${LIBC_SRCTOP}/${MACHINE_ARCH:S/powerpc64le/powerpc64/})
+LIBC_ARCH=${MACHINE_ARCH:S/powerpc64le/powerpc64/}
+.else
+LIBC_ARCH=${MACHINE_CPUARCH}
+.endif
+
+CFLAGS+= -I${SRCTOP}/libexec/rtld-elf/rtld-libc
+
+# Build all the libc files that use interposed symbols or pthreads again for
+# RTLD. We compile with a different libc_private.h and namespace.h that
+# redirects all calls to interposed functions to use the non-interposed version
+# instead.
+.PATH: ${LIBC_SRCTOP}/gen
+SRCS+= fdopendir.c opendir.c opendir2.c closedir.c readdir.c telldir.c
+
+# Avoid further dependencies by providing simple implementations of libc
+# functions such as __error(), etc.
+.PATH: ${SRCTOP}/libexec/rtld-elf/rtld-libc
+SRCS+= rtld_libc.c
+
+# Now build the remaining files from libc:
+.PATH: ${LIBC_SRCTOP}/stdlib
+SRCS+= reallocf.c realpath.c merge.c reallocarray.c
+# TODO: fix merge.c to build with WARNS=6
+.if ${COMPILER_TYPE} == "clang"
+CFLAGS.merge.c+=-Wno-error=null-pointer-arithmetic
+.endif
+.PATH: ${LIBC_SRCTOP}/gen
+SRCS+= errlst.c getcwd.c getprogname.c raise.c sigsetops.c sysctlnametomib.c \
+ __xuname.c
+# errlst.c needs the errlst.h header from libc:
+CFLAGS.errlst.c+=-I${LIBC_SRCTOP}/include
+
+# use generic versions of string functions to avoid potential ifunc dispatch
+.PATH: ${LIBC_SRCTOP}/string
+SRCS+= bcopy.c bzero.c memchr.c memcmp.c memcpy.c memmove.c memset.c strcat.c \
+ strchr.c strchrnul.c strcmp.c strcpy.c strcspn.c strdup.c strlcat.c \
+ strlcpy.c strlen.c strncmp.c strncpy.c strrchr.c strsep.c strspn.c \
+ strstr.c strtok.c
+CFLAGS.memchr.c+=-Wno-cast-qual
+CFLAGS.strchr.c+=-Wno-cast-qual
+CFLAGS.strchrnul.c+=-Wno-cast-qual
+CFLAGS.strcspn.c+=-Wno-sign-compare
+CFLAGS.strrchr.c+=-Wno-cast-qual
+CFLAGS.strspn.c+=-Wno-sign-compare
+CFLAGS.strstr.c+=-Wno-cast-qual -Wno-sign-compare
+CFLAGS.strtok.c+=-Wno-cast-qual
+
+# Also use all the syscall .o files from libsys_pic (libsys is always NO_SSP):
+_libsys_other_objects= fstat fstatat fstatfs syscall \
+ cerror geteuid getegid sigfastblock munmap mprotect \
+ sysarch __sysctl issetugid __getcwd utrace getpid \
+ thr_self thr_kill pread mmap lseek _exit \
+ getdirentries _close _fcntl _open _openat _read \
+ _sigprocmask _write readlink ___realpathat
+# A few other bits from libc_nossp_pic:
+_libc_other_objects= sigsetjmp lstat stat _setjmp setjmp setjmperr
+
+# Finally add additional architecture-dependent libc and libsys dependencies
+.if ${LIBC_ARCH} == "arm"
+# ARM needs aeabi_unwind_cpp for _setjmp
+_libc_other_objects+=aeabi_unwind_cpp
+.elif ${LIBC_ARCH} == "i386"
+# i386 needs i386_set_gsbase for allocate_initial_tls()
+_libsys_other_objects+=i386_set_gsbase
+.elif ${LIBC_ARCH} == "powerpc" || ${LIBC_ARCH} == "powerpcspe"
+# ppc needs __syncicache and abs for reloc.c
+_libc_other_objects+=syncicache abs
+.elif ${LIBC_ARCH} == "powerpc64"
+# ppc64 needs __syncicache for reloc.c
+_libc_other_objects+=syncicache
+.endif
+
+# Extract all the .o files from libc_nossp_pic.a and libsys_pic.a. This
+# ensures that we don't accidentally pull in the interposing table or
+# similar by linking directly against libc_nossp_pic.a
+_rtld_libc_objs=
+.for _obj in ${_libc_other_objects}
+_rtld_libc_objs+=${_obj}.nossppico
+CLEANFILES+=${_obj}.nossppico
+# LDFLAGS+= -Wl,--trace-symbol=${_obj}
+.endfor
+_rtld_libsys_objs=
+.for _obj in ${_libsys_other_objects}
+_rtld_libsys_objs+=${_obj}.pico
+CLEANFILES+=${_obj}.pico
+# LDFLAGS+= -Wl,--trace-symbol=${_obj}
+.endfor
+# LDFLAGS+= -Wl,--trace
+
+# We insert all the .o files from libc_nossp_pic.a into a new rtld_libc.a file
+# to ensure that only .o files that are actually used end up being included.
+#
+# XXX: we add dependencies on the source libraries in ../Makefile after
+# bsd.prog.mk includes bsd.libnames.mk since variables in dependencies are
+# expanded when parsed.
+rtld_libc.a: ${SRCTOP}/libexec/rtld-elf/rtld-libc/Makefile.inc
+ @rm -f ${.TARGET}
+ ${AR} x ${LIBC_NOSSP_PIC} ${_rtld_libc_objs}
+ ${AR} x ${LIBSYS_PIC} ${_rtld_libsys_objs}
+ ${AR} cr ${.TARGET} ${_rtld_libc_objs} ${_rtld_libsys_objs}
+CLEANFILES+=rtld_libc.a
+LDADD+=${.OBJDIR}/rtld_libc.a
+beforelinking: rtld_libc.a
diff --git a/libexec/rtld-elf/rtld-libc/libc_private.h b/libexec/rtld-elf/rtld-libc/libc_private.h
new file mode 100644
index 000000000000..0cfb0e562733
--- /dev/null
+++ b/libexec/rtld-elf/rtld-libc/libc_private.h
@@ -0,0 +1,37 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2019 Alex Richardson <arichardson@FreeBSD.org>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef _RTLD_LIBC_PRIVATE_H_
+#define _RTLD_LIBC_PRIVATE_H_
+
+#include "rtld_libc.h"
+
+#endif /* _RTLD_LIBC_PRIVATE_H_ */
diff --git a/libexec/rtld-elf/rtld-libc/namespace.h b/libexec/rtld-elf/rtld-libc/namespace.h
new file mode 100644
index 000000000000..571489d6ff19
--- /dev/null
+++ b/libexec/rtld-elf/rtld-libc/namespace.h
@@ -0,0 +1,37 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2019 Alex Richardson <arichardson@FreeBSD.org>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#define fcntl _fcntl
+#define open _open
+#define openat _openat
+#define fstatfs _fstatfs
+#define close _close
+#define getdirentries _getdirentries
diff --git a/libexec/rtld-elf/rtld-libc/rtld_libc.c b/libexec/rtld-elf/rtld-libc/rtld_libc.c
new file mode 100644
index 000000000000..a5371de394e9
--- /dev/null
+++ b/libexec/rtld-elf/rtld-libc/rtld_libc.c
@@ -0,0 +1,120 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2019 Alex Richardson <arichardson@FreeBSD.org>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <assert.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "rtld.h"
+#include "rtld_printf.h"
+#include "rtld_libc.h"
+
+/*
+ * Avoid dependencies from various libc calls on abort(). Since this is only
+ * used for assertions in RTLD, we can just raise SIGABRT directly.
+ */
+void
+abort(void)
+{
+ raise(SIGABRT);
+ __builtin_trap();
+}
+
+static int rtld_errno;
+int *__error(void);
+int *
+__error(void)
+{
+
+ return (&rtld_errno);
+}
+
+/* Avoid dependency on __libc_interposing, use the system call directly. */
+#undef sigprocmask
+int
+sigprocmask(int how, const sigset_t *set, sigset_t *oset)
+{
+
+ return (__sys_sigprocmask(how, set, oset));
+}
+__strong_reference(sigprocmask, __libc_sigprocmask);
+
+#if defined DEBUG || !defined(NDEBUG)
+/* Provide an implementation of __assert that does not pull in fprintf() */
+void
+__assert(const char *func, const char *file, int line, const char *failedexpr)
+{
+
+ if (func == NULL)
+ (void)rtld_fdprintf(STDERR_FILENO,
+ "Assertion failed: (%s), file %s, line %d.\n", failedexpr,
+ file, line);
+ else
+ (void)rtld_fdprintf(STDERR_FILENO,
+ "Assertion failed: (%s), function %s, file %s, line %d.\n",
+ failedexpr, func, file, line);
+ abort();
+ /* NOTREACHED */
+}
+#endif
+
+/*
+ * Avoid pulling in all of pthreads from getpagesize().
+ * It normally uses libc/gen/auxv.c which pulls in pthread_once().
+ * This relies on init_pagesizes setting page_size so must not be called
+ * before that.
+ */
+int
+getpagesize(void)
+{
+ return (page_size);
+}
+
+int __sys___sysctl(const int *name, u_int namelen, void *oldp,
+ size_t *oldlenp, const void *newp, size_t newlen);
+
+int
+sysctl(const int *name, u_int namelen, void *oldp, size_t *oldlenp,
+ const void *newp, size_t newlen)
+{
+ int retval;
+
+ assert(name[0] != CTL_USER && "Not supported inside rtld!");
+ retval = __sys___sysctl(name, namelen, oldp, oldlenp, newp, newlen);
+ return (retval);
+}
diff --git a/libexec/rtld-elf/rtld-libc/rtld_libc.h b/libexec/rtld-elf/rtld-libc/rtld_libc.h
new file mode 100644
index 000000000000..33601d73cbf9
--- /dev/null
+++ b/libexec/rtld-elf/rtld-libc/rtld_libc.h
@@ -0,0 +1,86 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2019 Alex Richardson <arichardson@FreeBSD.org>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef _RTLD_AVOID_LIBC_DEPS_H_
+#define _RTLD_AVOID_LIBC_DEPS_H_
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+
+/* Avoid dependencies on libthr (used by closedir/opendir/readdir) */
+#define __isthreaded 0
+#define _pthread_mutex_lock(mtx) (void)0
+#define _pthread_mutex_unlock(mtx) (void)0
+#define _pthread_mutex_destroy(mtx) (void)0
+#define __libc_interposing error, must not use this variable inside rtld
+
+int __sys_close(int);
+void __sys__exit(int) __dead2;
+int __sys_fcntl(int, int, ...);
+int __sys_fstat(int fd, struct stat *);
+int __sys_fstatat(int, const char *, struct stat *, int);
+int __sys___getcwd(char *, size_t);
+int __sys_open(const char *, int, ...);
+int __sys_openat(int, const char *, int, ...);
+int __sys_sigprocmask(int, const sigset_t *, sigset_t *);
+int __sys_thr_kill(long, int);
+int __sys_thr_self(long *);
+__ssize_t __sys_pread(int, void *, __size_t, __off_t);
+__ssize_t __sys_read(int, void *, __size_t);
+__ssize_t __sys_write(int, const void *, __size_t);
+
+extern char* __progname;
+const char *_getprogname(void);
+int __getosreldate(void);
+
+
+/*
+ * Don't pull in any of the libc wrappers. Instead we use the system call
+ * directly inside RTLD to avoid pulling in __libc_interposing (which pulls
+ * in lots more object files).
+ */
+#define close(fd) __sys_close(fd)
+#define _close(fd) __sys_close(fd)
+#define exit(status) __sys__exit(status)
+#define _exit(status) __sys__exit(status)
+#define fcntl(fd, cmd, arg) __sys_fcntl(fd, cmd, arg)
+#define _fcntl(fd, cmd, arg) __sys_fcntl(fd, cmd, arg)
+#define _fstat(fd, sb) __sys_fstat(fd, sb)
+#define open(path, ...) __sys_open(path, __VA_ARGS__)
+#define pread(fd, buf, nbytes, offset) __sys_pread(fd, buf, nbytes, offset)
+#define read(fd, buf, nbytes) __sys_read(fd, buf, nbytes)
+#define sigprocmask(how, set, oset) __sys_sigprocmask(how, set, oset)
+#define strerror(errno) rtld_strerror(errno)
+#define _write(fd, buf, nbytes) __sys_write(fd, buf, nbytes)
+#define write(fd, buf, nbytes) __sys_write(fd, buf, nbytes)
+
+#endif /* _RTLD_AVOID_LIBC_DEPS_H_ */
diff --git a/libexec/rtld-elf/rtld-libc/un-namespace.h b/libexec/rtld-elf/rtld-libc/un-namespace.h
new file mode 100644
index 000000000000..dcec40b4a27f
--- /dev/null
+++ b/libexec/rtld-elf/rtld-libc/un-namespace.h
@@ -0,0 +1,39 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2019 Alex Richardson <arichardson@FreeBSD.org>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#undef fcntl
+#undef open
+#undef openat
+#undef close
+#undef fstatfs
+#undef getdirentries
+
+#include "rtld_libc.h"
diff --git a/libexec/rtld-elf/rtld.1 b/libexec/rtld-elf/rtld.1
new file mode 100644
index 000000000000..62e4fc5676c2
--- /dev/null
+++ b/libexec/rtld-elf/rtld.1
@@ -0,0 +1,543 @@
+.\" Copyright (c) 1995 Paul Kranenburg
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by Paul Kranenburg.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd July 24, 2024
+.Dt RTLD 1
+.Os
+.Sh NAME
+.Nm ld-elf.so.1 ,
+.Nm ld.so ,
+.Nm rtld
+.Nd run-time link-editor
+.Sh DESCRIPTION
+The
+.Nm
+utility is a self-contained shared object providing run-time
+support for loading and link-editing shared objects into a process'
+address space.
+It is also commonly known as the dynamic linker.
+It uses the data structures
+contained within dynamically linked programs to determine which shared
+libraries are needed and loads them using the
+.Xr mmap 2
+system call.
+.Pp
+After all shared libraries have been successfully loaded,
+.Nm
+proceeds to resolve external references from both the main program and
+all objects loaded.
+A mechanism is provided for initialization routines
+to be called on a per-object basis, giving a shared object an opportunity
+to perform any extra set-up before execution of the program proper begins.
+This is useful for C++ libraries that contain static constructors.
+.Pp
+When resolving dependencies for the loaded objects,
+.Nm
+translates dynamic token strings in rpath and soname.
+If the
+.Fl "z origin"
+option of the static linker was set when linking the binary,
+the token expansion is performed at the object load time, see
+.Xr ld 1 .
+The following strings are recognized now:
+.Bl -tag -width ".Pa $PLATFORM"
+.It Pa $ORIGIN
+Translated to the full path of the loaded object.
+.It Pa $OSNAME
+Translated to the name of the operating system implementation.
+.It Pa $OSREL
+Translated to the release level of the operating system.
+.It Pa $PLATFORM
+Translated to the machine hardware platform.
+.It Pa $LIB
+Translated to the system library path component on the platform.
+It is
+.Pa lib
+for native binaries, and typically
+.Pa lib32
+for compat32 binaries.
+Other translations might exist for other ABIs supported on the platform.
+.El
+.Pp
+The
+.Nm
+utility itself is loaded by the kernel together with any dynamically-linked
+program that is to be executed.
+The kernel transfers control to the
+dynamic linker.
+After the dynamic linker has finished loading,
+relocating, and initializing the program and its required shared
+objects, it transfers control to the entry point of the program.
+The following search order is used to locate required shared objects:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+.Dv DT_RPATH
+of the referencing object unless that object also contains a
+.Dv DT_RUNPATH
+tag
+.It
+.Dv DT_RPATH
+of the program unless the referencing object contains a
+.Dv DT_RUNPATH
+tag
+.It
+Path indicated by
+.Ev LD_LIBRARY_PATH
+environment variable
+.It
+.Dv DT_RUNPATH
+of the referencing object
+.It
+Hints file produced by the
+.Xr ldconfig 8
+utility
+.It
+The
+.Pa /lib
+and
+.Pa /usr/lib
+directories, unless the referencing object was linked using the
+.Dq Fl z Ar nodefaultlib
+option
+.El
+.Pp
+The
+.Nm
+utility
+recognizes a number of environment variables that can be used to modify
+its behaviour.
+On 64-bit architectures, the linker for 32-bit objects recognizes
+all the environment variables listed below, but is being prefixed with
+.Ev LD_32_ ,
+for example:
+.Ev LD_32_TRACE_LOADED_OBJECTS .
+If the activated image is setuid or setgid, the variables are ignored.
+.Pp
+The run-time linker is able to access the environment provided
+at process startup.
+After startup, environment variables are maintained by higher-level
+libraries and are not accessible by the run-time linker.
+At run-time, effective settings can be queried using
+.Xr rtld_get_var 3 ,
+and some of them can be changed with
+.Xr rtld_set_var 3 .
+.Bl -tag -width ".Ev LD_LIBMAP_DISABLE"
+.It Ev LD_DUMP_REL_POST
+If set,
+.Nm
+will print a table containing all relocations after symbol
+binding and relocation.
+.It Ev LD_DUMP_REL_PRE
+If set,
+.Nm
+will print a table containing all relocations before symbol
+binding and relocation.
+.It Ev LD_DYNAMIC_WEAK
+If set, use the ELF standard-compliant symbol lookup behavior:
+resolve to the first found symbol definition.
+.Pp
+By default,
+.Fx
+provides the non-standard symbol lookup behavior:
+when a weak symbol definition is found, remember the definition and
+keep searching in the remaining shared objects for a non-weak definition.
+If found, the non-weak definition is preferred, otherwise the remembered
+weak definition is returned.
+.Pp
+Symbols exported by dynamic linker itself (see
+.Xr dlfcn 3 )
+are always resolved using
+.Fx
+rules regardless of the presence of the variable.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_LIBMAP
+A library replacement list in the same format as
+.Xr libmap.conf 5 .
+For convenience, the characters
+.Ql =
+and
+.Ql \&,
+can be used instead of a space and a newline.
+This variable is parsed after
+.Xr libmap.conf 5 ,
+and will override its entries.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_LIBMAP_DISABLE
+If set, disables the use of
+.Xr libmap.conf 5
+and
+.Ev LD_LIBMAP .
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_ELF_HINTS_PATH
+This variable will override the default location of
+.Dq hints
+file.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_LIBRARY_PATH
+A colon separated list of directories, overriding the default search path
+for shared libraries.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_LIBRARY_PATH_RPATH
+If the variable is specified and has a value starting with
+any of \'y\', \'Y\' or \'1\' symbols, the path specified by
+.Ev LD_LIBRARY_PATH
+variable is allowed to override the path from
+.Dv DT_RPATH
+for binaries which does not contain
+.Dv DT_RUNPATH
+tag.
+For such binaries, when the variable
+.Ev LD_LIBRARY_PATH_RPATH
+is set,
+.Dq Fl z Ar nodefaultlib
+link-time option is ignored as well.
+.It Ev LD_PRELOAD
+A list of shared libraries, separated by colons and/or white space,
+to be linked in before any
+other shared libraries.
+If the directory is not specified then
+the directories specified by
+.Ev LD_LIBRARY_PATH
+will be searched first
+followed by the set of built-in standard directories.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_PRELOAD_FDS
+A colon separated list of file descriptor numbers for libraries.
+This is intended for preloading libraries in which we already have a file
+descriptor.
+This may optimize the process of loading libraries because we do not have to
+look for them in directories.
+It may also be useful in a capability base system where we do not have access to
+global namespaces such as the filesystem.
+.It Ev LD_LIBRARY_PATH_FDS
+A colon separated list of file descriptor numbers for library directories.
+This is intended for use within
+.Xr capsicum 4
+sandboxes, when global namespaces such as the filesystem are unavailable.
+It is consulted just after LD_LIBRARY_PATH.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_BIND_NOT
+When set to a nonempty string, prevents modifications of the PLT slots when
+doing bindings.
+As result, each call of the PLT-resolved function is resolved.
+In combination with debug output, this provides complete account of
+all bind actions at runtime.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_BIND_NOW
+When set to a nonempty string, causes
+.Nm
+to relocate all external function calls before starting execution of the
+program.
+Normally, function calls are bound lazily, at the first call
+of each function.
+.Ev LD_BIND_NOW
+increases the start-up time of a program, but it avoids run-time
+surprises caused by unexpectedly undefined functions.
+.It Ev LD_TRACE_LOADED_OBJECTS
+When set to a nonempty string, causes
+.Nm
+to exit after loading the shared objects and printing a summary which includes
+the absolute pathnames of all objects, to standard output.
+.It Ev LD_TRACE_LOADED_OBJECTS_ALL
+When set to a nonempty string, causes
+.Nm
+to expand the summary to indicate which objects caused each object to
+be loaded.
+.It Ev LD_TRACE_LOADED_OBJECTS_FMT1
+.It Ev LD_TRACE_LOADED_OBJECTS_FMT2
+When set, these variables are interpreted as format strings a la
+.Xr printf 3
+to customize the trace output and are used by
+.Xr ldd 1 Ns 's
+.Fl f
+option and allows
+.Xr ldd 1
+to be operated as a filter more conveniently.
+If the dependency name starts with string
+.Pa lib ,
+.Ev LD_TRACE_LOADED_OBJECTS_FMT1
+is used, otherwise
+.Ev LD_TRACE_LOADED_OBJECTS_FMT2
+is used.
+The following conversions can be used:
+.Bl -tag -width 4n
+.It Li %a
+The main program's name
+(also known as
+.Dq __progname ) .
+.It Li \&%A
+The value of the environment variable
+.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME .
+Typically used to print both the names of programs and shared libraries
+being inspected using
+.Xr ldd 1 .
+.It Li %o
+The library name.
+.It Li %p
+The full pathname as determined by
+.Nm rtld Ns 's
+library search rules.
+.It Li %x
+The library's load address.
+.El
+.Pp
+Additionally,
+.Ql \en
+and
+.Ql \et
+are recognized and have their usual meaning.
+.It Ev LD_UTRACE
+If set,
+.Nm
+will log events such as the loading and unloading of shared objects via
+.Xr utrace 2 .
+.It Ev LD_LOADFLTR
+If set,
+.Nm
+will process the filtee dependencies of the loaded objects immediately,
+instead of postponing it until required.
+Normally, the filtees are opened at the time of the first symbol resolution
+from the filter object.
+.It Ev LD_SHOW_AUXV
+If set, causes
+.Nm
+to dump content of the aux vector to standard output, before passing
+control to any user code.
+.It Ev LD_STATIC_TLS_EXTRA
+If the variable is specified and has a numeric value,
+.Nm
+will set the size of the static TLS extra space to the specified number
+of bytes.
+The static TLS extra space is used when loading objects compiled for
+initial-exec TLS code model with
+.Xr dlopen 3 .
+The minimum value that can be specified is \'128\'.
+.It Ev LD_NO_DL_ITERATE_PHDR_AFTER_FORK
+Allow
+.Xr dl_iterate_phdr 3
+to block in callback, without causing deadlock with the
+.Xr fork 2 .
+The drawback is that the image started in this mode cannot use
+.Xr dl_iterate_phdr 3
+after fork.
+.El
+.Sh DIRECT EXECUTION MODE
+.Nm
+is typically used implicitly, loaded by the kernel as requested by the
+.Dv PT_INTERP
+program header of the executed binary.
+.Fx
+also supports a direct execution mode for the dynamic linker.
+In this mode, the user explicitly executes
+.Nm
+and provides the path of the program to be linked and executed as
+an argument.
+This mode allows use of a non-standard dynamic linker for a program
+activation without changing the binary or without changing
+the installed dynamic linker.
+Execution options may be specified.
+.Pp
+The syntax of the direct invocation is
+.Bd -ragged -offset indent
+.Pa /libexec/ld-elf.so.1
+.Op Fl b Ar exe
+.Op Fl d
+.Op Fl f Ar fd
+.Op Fl o Ar OPT=VALUE
+.Op Fl p
+.Op Fl u
+.Op Fl v
+.Op Fl -
+.Pa image_path
+.Op Ar image arguments
+.Ed
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl b Ar exe
+Use the executable
+.Fa exe
+instead of
+.Fa image_path
+for activation.
+If this option is specified,
+.Ar image_path
+is only used to provide the
+.Va argv[0]
+value to the program.
+.It Fl d
+Turn off the emulation of the binary execute permission.
+.It Fl f Ar fd
+File descriptor
+.Ar fd
+references the binary to be activated by
+.Nm .
+It must already be opened in the process when executing
+.Nm .
+If this option is specified,
+.Ar image_path
+is only used to provide the
+.Va argv[0]
+value to the program.
+.It Fl o Ar OPT=VALUE
+Set the
+.Ar OPT
+configuration variable to the value
+.Ar VALUE .
+The possible variable names are listed above as
+.Ev LD_
+prefixed environment variables, but here are referenced without the
+.Ev LD_
+prefix.
+A configuration variable set this way does not leak into
+the activated image's environment.
+.Pp
+The option can be repeated as many times as needed to set
+all configuration parameters.
+The parameters set using this option have priority over
+the same parameters assigned via environment.
+.It Fl p
+If the
+.Pa image_path
+argument specifies a name which does not contain a slash
+.Dq Li /
+character,
+.Nm
+uses the search path provided by the environment variable
+.Dv PATH
+to find the binary to execute.
+.It Fl u
+Ignore all
+.Ev LD_
+environment variables and previous command line
+.Fl o
+options that otherwise affect the dynamic
+linker behavior.
+.It Fl v
+Display information about this run-time linker binary, then exit.
+.It Fl -
+Ends the
+.Nm
+options.
+The argument following
+.Fl -
+is interpreted as the path of the binary to execute.
+.El
+.Pp
+In the direct execution mode,
+.Nm
+emulates verification of the binary execute permission for the
+current user.
+This is done to avoid breaking user expectations in naively restricted
+execution environments.
+The verification only uses Unix
+.Dv DACs ,
+ignores
+.Dv ACLs ,
+and is naturally prone to race conditions.
+Environments which rely on such restrictions are weak
+and breakable on their own.
+It can be turned off with the
+.Fl d
+option.
+.Sh VERSIONING
+Newer
+.Nm
+might provide some features or changes in runtime behavior that cannot be
+easily detected at runtime by checking of the normal exported symbols.
+Note that it is almost always wrong to verify
+.Dv __FreeBSD_version
+in userspace to detect features, either at compile or at run time,
+because either kernel, or libc, or environment variables could not
+match the running
+.Nm .
+.Pp
+To solve the problem,
+.Nm
+exports some feature indicators in the
+.Fx
+private symbols namespace
+.Dv FBSDprivate_1.0 .
+Symbols start with the
+.Dv _rtld_version
+prefix.
+Current list of defined symbols and corresponding features is:
+.Bl -tag -width indent
+.It Dv _rtld_version__FreeBSD_version
+Symbol exports the value of the
+.Dv __FreeBSD_version
+definition as it was provided during the
+.Nm
+build.
+The symbol is always present since the
+.Dv _rtld_version
+facility was introduced.
+.It Dv _rtld_version_laddr_offset
+The
+.Va l_addr
+member of the
+.Vt link_map
+structure contains the load offset of the shared object.
+Before that,
+.Va l_addr
+contained the base address of the library.
+See
+.Xr dlinfo 3 .
+.Pp
+Also it indicates the presence of
+.Va l_refname
+member of the structure.
+.It Dv _rtld_version_dlpi_tls_data
+The
+.Va dlpi_tls_data
+member of the structure
+.Vt dl_phdr_info
+contains the address of the module TLS segment for the calling thread,
+and not the address of the initialization segment.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/ld-elf32.so.hints" -compact
+.It Pa /var/run/ld-elf.so.hints
+Hints file.
+.It Pa /var/run/ld-elf32.so.hints
+Hints file for 32-bit binaries on 64-bit system.
+.It Pa /etc/libmap.conf
+The libmap configuration file.
+.It Pa /etc/libmap32.conf
+The libmap configuration file for 32-bit binaries on 64-bit system.
+.El
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr ldd 1 ,
+.Xr dlinfo 3 ,
+.Xr rtld_get_var 3 ,
+.Xr capsicum 4 ,
+.Xr elf 5 ,
+.Xr libmap.conf 5 ,
+.Xr ldconfig 8
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
new file mode 100644
index 000000000000..d27af520c21d
--- /dev/null
+++ b/libexec/rtld-elf/rtld.c
@@ -0,0 +1,6737 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
+ * Copyright 2003 Alexander Kabaev <kan@FreeBSD.ORG>.
+ * Copyright 2009-2013 Konstantin Belousov <kib@FreeBSD.ORG>.
+ * Copyright 2012 John Marino <draco@marino.st>.
+ * Copyright 2014-2017 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Konstantin Belousov
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Dynamic linker for ELF.
+ *
+ * John Polstra <jdp@polstra.com>.
+ */
+
+#include <sys/param.h>
+#include <sys/ktrace.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "libmap.h"
+#include "notes.h"
+#include "rtld.h"
+#include "rtld_libc.h"
+#include "rtld_malloc.h"
+#include "rtld_paths.h"
+#include "rtld_printf.h"
+#include "rtld_tls.h"
+#include "rtld_utrace.h"
+
+/* Types. */
+typedef void (*func_ptr_type)(void);
+typedef void *(*path_enum_proc)(const char *path, size_t len, void *arg);
+
+/* Variables that cannot be static: */
+extern struct r_debug r_debug; /* For GDB */
+extern int _thread_autoinit_dummy_decl;
+extern void (*__cleanup)(void);
+
+struct dlerror_save {
+ int seen;
+ char *msg;
+};
+
+struct tcb_list_entry {
+ TAILQ_ENTRY(tcb_list_entry) next;
+};
+
+/*
+ * Function declarations.
+ */
+static bool allocate_tls_offset_common(size_t *offp, size_t tlssize,
+ size_t tlsalign, size_t tlspoffset);
+static const char *basename(const char *);
+static void digest_dynamic1(Obj_Entry *, int, const Elf_Dyn **,
+ const Elf_Dyn **, const Elf_Dyn **);
+static bool digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *,
+ const Elf_Dyn *);
+static bool digest_dynamic(Obj_Entry *, int);
+static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
+static void distribute_static_tls(Objlist *);
+static Obj_Entry *dlcheck(void *);
+static int dlclose_locked(void *, RtldLockState *);
+static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj,
+ int lo_flags, int mode, RtldLockState *lockstate);
+static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int);
+static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *);
+static bool donelist_check(DoneList *, const Obj_Entry *);
+static void dump_auxv(Elf_Auxinfo **aux_info);
+static void errmsg_restore(struct dlerror_save *);
+static struct dlerror_save *errmsg_save(void);
+static void *fill_search_info(const char *, size_t, void *);
+static char *find_library(const char *, const Obj_Entry *, int *);
+static const char *gethints(bool);
+static void hold_object(Obj_Entry *);
+static void unhold_object(Obj_Entry *);
+static void init_dag(Obj_Entry *);
+static void init_marker(Obj_Entry *);
+static void init_pagesizes(Elf_Auxinfo **aux_info);
+static void init_rtld(caddr_t, Elf_Auxinfo **);
+static void initlist_add_neededs(Needed_Entry *, Objlist *, Objlist *);
+static void initlist_add_objects(Obj_Entry *, Obj_Entry *, Objlist *,
+ Objlist *);
+static void initlist_for_loaded_obj(Obj_Entry *obj, Obj_Entry *tail,
+ Objlist *list);
+static int initlist_objects_ifunc(Objlist *, bool, int, RtldLockState *);
+static void linkmap_add(Obj_Entry *);
+static void linkmap_delete(Obj_Entry *);
+static void load_filtees(Obj_Entry *, int flags, RtldLockState *);
+static void unload_filtees(Obj_Entry *, RtldLockState *);
+static int load_needed_objects(Obj_Entry *, int);
+static int load_preload_objects(const char *, bool);
+static int load_kpreload(const void *addr);
+static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int);
+static void map_stacks_exec(RtldLockState *);
+static int obj_disable_relro(Obj_Entry *);
+static int obj_enforce_relro(Obj_Entry *);
+static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *);
+static void objlist_call_init(Objlist *, RtldLockState *);
+static void objlist_clear(Objlist *);
+static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *);
+static void objlist_init(Objlist *);
+static void objlist_push_head(Objlist *, Obj_Entry *);
+static void objlist_push_tail(Objlist *, Obj_Entry *);
+static void objlist_put_after(Objlist *, Obj_Entry *, Obj_Entry *);
+static void objlist_remove(Objlist *, Obj_Entry *);
+static int open_binary_fd(const char *argv0, bool search_in_path,
+ const char **binpath_res);
+static int parse_args(char *argv[], int argc, bool *use_pathp, int *fdp,
+ const char **argv0, bool *dir_ignore);
+static int parse_integer(const char *);
+static void *path_enumerate(const char *, path_enum_proc, const char *, void *);
+static void print_usage(const char *argv0);
+static void release_object(Obj_Entry *);
+static int relocate_object_dag(Obj_Entry *root, bool bind_now,
+ Obj_Entry *rtldobj, int flags, RtldLockState *lockstate);
+static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj,
+ int flags, RtldLockState *lockstate);
+static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, int,
+ RtldLockState *);
+static int resolve_object_ifunc(Obj_Entry *, bool, int, RtldLockState *);
+static int rtld_dirname(const char *, char *);
+static int rtld_dirname_abs(const char *, char *);
+static void *rtld_dlopen(const char *name, int fd, int mode);
+static void rtld_exit(void);
+static void rtld_nop_exit(void);
+static char *search_library_path(const char *, const char *, const char *,
+ int *);
+static char *search_library_pathfds(const char *, const char *, int *);
+static const void **get_program_var_addr(const char *, RtldLockState *);
+static void set_program_var(const char *, const void *);
+static int symlook_default(SymLook *, const Obj_Entry *refobj);
+static int symlook_global(SymLook *, DoneList *);
+static void symlook_init_from_req(SymLook *, const SymLook *);
+static int symlook_list(SymLook *, const Objlist *, DoneList *);
+static int symlook_needed(SymLook *, const Needed_Entry *, DoneList *);
+static int symlook_obj1_sysv(SymLook *, const Obj_Entry *);
+static int symlook_obj1_gnu(SymLook *, const Obj_Entry *);
+static void *tls_get_addr_slow(struct tcb *, int, size_t, bool) __noinline;
+static void trace_loaded_objects(Obj_Entry *, bool);
+static void unlink_object(Obj_Entry *);
+static void unload_object(Obj_Entry *, RtldLockState *lockstate);
+static void unref_dag(Obj_Entry *);
+static void ref_dag(Obj_Entry *);
+static char *origin_subst_one(Obj_Entry *, char *, const char *, const char *,
+ bool);
+static char *origin_subst(Obj_Entry *, const char *);
+static bool obj_resolve_origin(Obj_Entry *obj);
+static void preinit_main(void);
+static int rtld_verify_versions(const Objlist *);
+static int rtld_verify_object_versions(Obj_Entry *);
+static void object_add_name(Obj_Entry *, const char *);
+static int object_match_name(const Obj_Entry *, const char *);
+static void ld_utrace_log(int, void *, void *, size_t, int, const char *);
+static void rtld_fill_dl_phdr_info(const Obj_Entry *obj,
+ struct dl_phdr_info *phdr_info);
+static uint32_t gnu_hash(const char *);
+static bool matched_symbol(SymLook *, const Obj_Entry *, Sym_Match_Result *,
+ const unsigned long);
+
+void r_debug_state(struct r_debug *, struct link_map *) __noinline __exported;
+void _r_debug_postinit(struct link_map *) __noinline __exported;
+
+int __sys_openat(int, const char *, int, ...);
+
+/*
+ * Data declarations.
+ */
+struct r_debug r_debug __exported; /* for GDB; */
+static bool libmap_disable; /* Disable libmap */
+static bool ld_loadfltr; /* Immediate filters processing */
+static const char *libmap_override; /* Maps to use in addition to libmap.conf */
+static bool trust; /* False for setuid and setgid programs */
+static bool dangerous_ld_env; /* True if environment variables have been
+ used to affect the libraries loaded */
+bool ld_bind_not; /* Disable PLT update */
+static const char *ld_bind_now; /* Environment variable for immediate binding */
+static const char *ld_debug; /* Environment variable for debugging */
+static bool ld_dynamic_weak = true; /* True if non-weak definition overrides
+ weak definition */
+static const char *ld_library_path; /* Environment variable for search path */
+static const char
+ *ld_library_dirs; /* Environment variable for library descriptors */
+static const char *ld_preload; /* Environment variable for libraries to
+ load first */
+static const char *ld_preload_fds; /* Environment variable for libraries
+ represented by descriptors */
+static const char
+ *ld_elf_hints_path; /* Environment variable for alternative hints path */
+static const char *ld_tracing; /* Called from ldd to print libs */
+static const char *ld_utrace; /* Use utrace() to log events. */
+static struct obj_entry_q obj_list; /* Queue of all loaded objects */
+static Obj_Entry *obj_main; /* The main program shared object */
+static Obj_Entry obj_rtld; /* The dynamic linker shared object */
+static unsigned int obj_count; /* Number of objects in obj_list */
+static unsigned int obj_loads; /* Number of loads of objects (gen count) */
+size_t ld_static_tls_extra = /* Static TLS extra space (bytes) */
+ RTLD_STATIC_TLS_EXTRA;
+
+static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */
+ STAILQ_HEAD_INITIALIZER(list_global);
+static Objlist list_main = /* Objects loaded at program startup */
+ STAILQ_HEAD_INITIALIZER(list_main);
+static Objlist list_fini = /* Objects needing fini() calls */
+ STAILQ_HEAD_INITIALIZER(list_fini);
+
+Elf_Sym sym_zero; /* For resolving undefined weak refs. */
+
+#define GDB_STATE(s, m) \
+ r_debug.r_state = s; \
+ r_debug_state(&r_debug, m);
+
+extern Elf_Dyn _DYNAMIC;
+#pragma weak _DYNAMIC
+
+int dlclose(void *) __exported;
+char *dlerror(void) __exported;
+void *dlopen(const char *, int) __exported;
+void *fdlopen(int, int) __exported;
+void *dlsym(void *, const char *) __exported;
+dlfunc_t dlfunc(void *, const char *) __exported;
+void *dlvsym(void *, const char *, const char *) __exported;
+int dladdr(const void *, Dl_info *) __exported;
+void dllockinit(void *, void *(*)(void *), void (*)(void *), void (*)(void *),
+ void (*)(void *), void (*)(void *), void (*)(void *)) __exported;
+int dlinfo(void *, int, void *) __exported;
+int _dl_iterate_phdr_locked(__dl_iterate_hdr_callback, void *) __exported;
+int dl_iterate_phdr(__dl_iterate_hdr_callback, void *) __exported;
+int _rtld_addr_phdr(const void *, struct dl_phdr_info *) __exported;
+int _rtld_get_stack_prot(void) __exported;
+int _rtld_is_dlopened(void *) __exported;
+void _rtld_error(const char *, ...) __exported;
+const char *rtld_get_var(const char *name) __exported;
+int rtld_set_var(const char *name, const char *val) __exported;
+
+/* Only here to fix -Wmissing-prototypes warnings */
+int __getosreldate(void);
+func_ptr_type _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp);
+Elf_Addr _rtld_bind(Obj_Entry *obj, Elf_Size reloff);
+
+int npagesizes;
+static int osreldate;
+size_t *pagesizes;
+size_t page_size;
+
+static int stack_prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+static int max_stack_flags;
+
+/*
+ * Global declarations normally provided by crt1. The dynamic linker is
+ * not built with crt1, so we have to provide them ourselves.
+ */
+char *__progname;
+char **environ;
+
+/*
+ * Used to pass argc, argv to init functions.
+ */
+int main_argc;
+char **main_argv;
+
+/*
+ * Globals to control TLS allocation.
+ */
+size_t tls_last_offset; /* Static TLS offset of last module */
+size_t tls_last_size; /* Static TLS size of last module */
+size_t tls_static_space; /* Static TLS space allocated */
+static size_t tls_static_max_align;
+Elf_Addr tls_dtv_generation = 1; /* Used to detect when dtv size changes */
+int tls_max_index = 1; /* Largest module index allocated */
+
+static TAILQ_HEAD(, tcb_list_entry) tcb_list =
+ TAILQ_HEAD_INITIALIZER(tcb_list);
+static size_t tcb_list_entry_offset;
+
+static bool ld_library_path_rpath = false;
+bool ld_fast_sigblock = false;
+
+/*
+ * Globals for path names, and such
+ */
+const char *ld_elf_hints_default = _PATH_ELF_HINTS;
+const char *ld_path_libmap_conf = _PATH_LIBMAP_CONF;
+const char *ld_path_rtld = _PATH_RTLD;
+const char *ld_standard_library_path = STANDARD_LIBRARY_PATH;
+const char *ld_env_prefix = LD_;
+
+static void (*rtld_exit_ptr)(void);
+
+/*
+ * Fill in a DoneList with an allocation large enough to hold all of
+ * the currently-loaded objects. Keep this as a macro since it calls
+ * alloca and we want that to occur within the scope of the caller.
+ */
+#define donelist_init(dlp) \
+ ((dlp)->objs = alloca(obj_count * sizeof(dlp)->objs[0]), \
+ assert((dlp)->objs != NULL), (dlp)->num_alloc = obj_count, \
+ (dlp)->num_used = 0)
+
+#define LD_UTRACE(e, h, mb, ms, r, n) \
+ do { \
+ if (ld_utrace != NULL) \
+ ld_utrace_log(e, h, mb, ms, r, n); \
+ } while (0)
+
+static void
+ld_utrace_log(int event, void *handle, void *mapbase, size_t mapsize,
+ int refcnt, const char *name)
+{
+ struct utrace_rtld ut;
+ static const char rtld_utrace_sig[RTLD_UTRACE_SIG_SZ] = RTLD_UTRACE_SIG;
+
+ memset(&ut, 0, sizeof(ut)); /* clear holes */
+ memcpy(ut.sig, rtld_utrace_sig, sizeof(ut.sig));
+ ut.event = event;
+ ut.handle = handle;
+ ut.mapbase = mapbase;
+ ut.mapsize = mapsize;
+ ut.refcnt = refcnt;
+ if (name != NULL)
+ strlcpy(ut.name, name, sizeof(ut.name));
+ utrace(&ut, sizeof(ut));
+}
+
+struct ld_env_var_desc {
+ const char *const n;
+ const char *val;
+ const bool unsecure : 1;
+ const bool can_update : 1;
+ const bool debug : 1;
+ bool owned : 1;
+};
+#define LD_ENV_DESC(var, unsec, ...) \
+ [LD_##var] = { .n = #var, .unsecure = unsec, __VA_ARGS__ }
+
+static struct ld_env_var_desc ld_env_vars[] = {
+ LD_ENV_DESC(BIND_NOW, false),
+ LD_ENV_DESC(PRELOAD, true),
+ LD_ENV_DESC(LIBMAP, true),
+ LD_ENV_DESC(LIBRARY_PATH, true, .can_update = true),
+ LD_ENV_DESC(LIBRARY_PATH_FDS, true, .can_update = true),
+ LD_ENV_DESC(LIBMAP_DISABLE, true),
+ LD_ENV_DESC(BIND_NOT, true),
+ LD_ENV_DESC(DEBUG, true, .can_update = true, .debug = true),
+ LD_ENV_DESC(ELF_HINTS_PATH, true),
+ LD_ENV_DESC(LOADFLTR, true),
+ LD_ENV_DESC(LIBRARY_PATH_RPATH, true, .can_update = true),
+ LD_ENV_DESC(PRELOAD_FDS, true),
+ LD_ENV_DESC(DYNAMIC_WEAK, true, .can_update = true),
+ LD_ENV_DESC(TRACE_LOADED_OBJECTS, false),
+ LD_ENV_DESC(UTRACE, false, .can_update = true),
+ LD_ENV_DESC(DUMP_REL_PRE, false, .can_update = true),
+ LD_ENV_DESC(DUMP_REL_POST, false, .can_update = true),
+ LD_ENV_DESC(TRACE_LOADED_OBJECTS_PROGNAME, false),
+ LD_ENV_DESC(TRACE_LOADED_OBJECTS_FMT1, false),
+ LD_ENV_DESC(TRACE_LOADED_OBJECTS_FMT2, false),
+ LD_ENV_DESC(TRACE_LOADED_OBJECTS_ALL, false),
+ LD_ENV_DESC(SHOW_AUXV, false),
+ LD_ENV_DESC(STATIC_TLS_EXTRA, false),
+ LD_ENV_DESC(NO_DL_ITERATE_PHDR_AFTER_FORK, false),
+};
+
+const char *
+ld_get_env_var(int idx)
+{
+ return (ld_env_vars[idx].val);
+}
+
+static const char *
+rtld_get_env_val(char **env, const char *name, size_t name_len)
+{
+ char **m, *n, *v;
+
+ for (m = env; *m != NULL; m++) {
+ n = *m;
+ v = strchr(n, '=');
+ if (v == NULL) {
+ /* corrupt environment? */
+ continue;
+ }
+ if (v - n == (ptrdiff_t)name_len &&
+ strncmp(name, n, name_len) == 0)
+ return (v + 1);
+ }
+ return (NULL);
+}
+
+static void
+rtld_init_env_vars_for_prefix(char **env, const char *env_prefix)
+{
+ struct ld_env_var_desc *lvd;
+ size_t prefix_len, nlen;
+ char **m, *n, *v;
+ int i;
+
+ prefix_len = strlen(env_prefix);
+ for (m = env; *m != NULL; m++) {
+ n = *m;
+ if (strncmp(env_prefix, n, prefix_len) != 0) {
+ /* Not a rtld environment variable. */
+ continue;
+ }
+ n += prefix_len;
+ v = strchr(n, '=');
+ if (v == NULL) {
+ /* corrupt environment? */
+ continue;
+ }
+ for (i = 0; i < (int)nitems(ld_env_vars); i++) {
+ lvd = &ld_env_vars[i];
+ if (lvd->val != NULL) {
+ /* Saw higher-priority variable name already. */
+ continue;
+ }
+ nlen = strlen(lvd->n);
+ if (v - n == (ptrdiff_t)nlen &&
+ strncmp(lvd->n, n, nlen) == 0) {
+ lvd->val = v + 1;
+ break;
+ }
+ }
+ }
+}
+
+static void
+rtld_init_env_vars(char **env)
+{
+ rtld_init_env_vars_for_prefix(env, ld_env_prefix);
+}
+
+static void
+set_ld_elf_hints_path(void)
+{
+ if (ld_elf_hints_path == NULL || strlen(ld_elf_hints_path) == 0)
+ ld_elf_hints_path = ld_elf_hints_default;
+}
+
+uintptr_t
+rtld_round_page(uintptr_t x)
+{
+ return (roundup2(x, page_size));
+}
+
+uintptr_t
+rtld_trunc_page(uintptr_t x)
+{
+ return (rounddown2(x, page_size));
+}
+
+/*
+ * Main entry point for dynamic linking. The first argument is the
+ * stack pointer. The stack is expected to be laid out as described
+ * in the SVR4 ABI specification, Intel 386 Processor Supplement.
+ * Specifically, the stack pointer points to a word containing
+ * ARGC. Following that in the stack is a null-terminated sequence
+ * of pointers to argument strings. Then comes a null-terminated
+ * sequence of pointers to environment strings. Finally, there is a
+ * sequence of "auxiliary vector" entries.
+ *
+ * The second argument points to a place to store the dynamic linker's
+ * exit procedure pointer and the third to a place to store the main
+ * program's object.
+ *
+ * The return value is the main program's entry point.
+ */
+func_ptr_type
+_rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
+{
+ Elf_Auxinfo *aux, *auxp, *auxpf, *aux_info[AT_COUNT], auxtmp;
+ Objlist_Entry *entry;
+ Obj_Entry *last_interposer, *obj, *preload_tail;
+ const Elf_Phdr *phdr;
+ Objlist initlist;
+ RtldLockState lockstate;
+ struct stat st;
+ Elf_Addr *argcp;
+ char **argv, **env, **envp, *kexecpath;
+ const char *argv0, *binpath, *library_path_rpath, *static_tls_extra;
+ struct ld_env_var_desc *lvd;
+ caddr_t imgentry;
+ char buf[MAXPATHLEN];
+ int argc, fd, i, mib[4], old_osrel, osrel, phnum, rtld_argc;
+ size_t sz;
+#ifdef __powerpc__
+ int old_auxv_format = 1;
+#endif
+ bool dir_enable, dir_ignore, direct_exec, explicit_fd, search_in_path;
+
+ /*
+ * On entry, the dynamic linker itself has not been relocated yet.
+ * Be very careful not to reference any global data until after
+ * init_rtld has returned. It is OK to reference file-scope statics
+ * and string constants, and to call static and global functions.
+ */
+
+ /* Find the auxiliary vector on the stack. */
+ argcp = sp;
+ argc = *sp++;
+ argv = (char **)sp;
+ sp += argc + 1; /* Skip over arguments and NULL terminator */
+ env = (char **)sp;
+ while (*sp++ != 0) /* Skip over environment, and NULL terminator */
+ ;
+ aux = (Elf_Auxinfo *)sp;
+
+ /* Digest the auxiliary vector. */
+ for (i = 0; i < AT_COUNT; i++)
+ aux_info[i] = NULL;
+ for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
+ if (auxp->a_type < AT_COUNT)
+ aux_info[auxp->a_type] = auxp;
+#ifdef __powerpc__
+ if (auxp->a_type == 23) /* AT_STACKPROT */
+ old_auxv_format = 0;
+#endif
+ }
+
+#ifdef __powerpc__
+ if (old_auxv_format) {
+ /* Remap from old-style auxv numbers. */
+ aux_info[23] = aux_info[21]; /* AT_STACKPROT */
+ aux_info[21] = aux_info[19]; /* AT_PAGESIZESLEN */
+ aux_info[19] = aux_info[17]; /* AT_NCPUS */
+ aux_info[17] = aux_info[15]; /* AT_CANARYLEN */
+ aux_info[15] = aux_info[13]; /* AT_EXECPATH */
+ aux_info[13] = NULL; /* AT_GID */
+
+ aux_info[20] = aux_info[18]; /* AT_PAGESIZES */
+ aux_info[18] = aux_info[16]; /* AT_OSRELDATE */
+ aux_info[16] = aux_info[14]; /* AT_CANARY */
+ aux_info[14] = NULL; /* AT_EGID */
+ }
+#endif
+
+ /* Initialize and relocate ourselves. */
+ assert(aux_info[AT_BASE] != NULL);
+ init_rtld((caddr_t)aux_info[AT_BASE]->a_un.a_ptr, aux_info);
+
+ dlerror_dflt_init();
+
+ __progname = obj_rtld.path;
+ argv0 = argv[0] != NULL ? argv[0] : "(null)";
+ environ = env;
+ main_argc = argc;
+ main_argv = argv;
+
+ if (aux_info[AT_BSDFLAGS] != NULL &&
+ (aux_info[AT_BSDFLAGS]->a_un.a_val & ELF_BSDF_SIGFASTBLK) != 0)
+ ld_fast_sigblock = true;
+
+ trust = !issetugid();
+ direct_exec = false;
+
+ md_abi_variant_hook(aux_info);
+ rtld_init_env_vars(env);
+
+ fd = -1;
+ if (aux_info[AT_EXECFD] != NULL) {
+ fd = aux_info[AT_EXECFD]->a_un.a_val;
+ } else {
+ assert(aux_info[AT_PHDR] != NULL);
+ phdr = (const Elf_Phdr *)aux_info[AT_PHDR]->a_un.a_ptr;
+ if (phdr == obj_rtld.phdr) {
+ if (!trust) {
+ _rtld_error(
+ "Tainted process refusing to run binary %s",
+ argv0);
+ rtld_die();
+ }
+ direct_exec = true;
+
+ dbg("opening main program in direct exec mode");
+ if (argc >= 2) {
+ rtld_argc = parse_args(argv, argc,
+ &search_in_path, &fd, &argv0, &dir_ignore);
+ explicit_fd = (fd != -1);
+ binpath = NULL;
+ if (!explicit_fd)
+ fd = open_binary_fd(argv0,
+ search_in_path, &binpath);
+ if (fstat(fd, &st) == -1) {
+ _rtld_error(
+ "Failed to fstat FD %d (%s): %s",
+ fd,
+ explicit_fd ?
+ "user-provided descriptor" :
+ argv0,
+ rtld_strerror(errno));
+ rtld_die();
+ }
+
+ /*
+ * Rough emulation of the permission checks done
+ * by execve(2), only Unix DACs are checked,
+ * ACLs are ignored. Preserve the semantic of
+ * disabling owner to execute if owner x bit is
+ * cleared, even if others x bit is enabled.
+ * mmap(2) does not allow to mmap with PROT_EXEC
+ * if binary' file comes from noexec mount. We
+ * cannot set a text reference on the binary.
+ */
+ dir_enable = false;
+ if (st.st_uid == geteuid()) {
+ if ((st.st_mode & S_IXUSR) != 0)
+ dir_enable = true;
+ } else if (st.st_gid == getegid()) {
+ if ((st.st_mode & S_IXGRP) != 0)
+ dir_enable = true;
+ } else if ((st.st_mode & S_IXOTH) != 0) {
+ dir_enable = true;
+ }
+ if (!dir_enable && !dir_ignore) {
+ _rtld_error(
+ "No execute permission for binary %s",
+ argv0);
+ rtld_die();
+ }
+
+ /*
+ * For direct exec mode, argv[0] is the
+ * interpreter name, we must remove it and shift
+ * arguments left before invoking binary main.
+ * Since stack layout places environment
+ * pointers and aux vectors right after the
+ * terminating NULL, we must shift environment
+ * and aux as well.
+ */
+ main_argc = argc - rtld_argc;
+ for (i = 0; i <= main_argc; i++)
+ argv[i] = argv[i + rtld_argc];
+ *argcp -= rtld_argc;
+ environ = env = envp = argv + main_argc + 1;
+ dbg("move env from %p to %p", envp + rtld_argc,
+ envp);
+ do {
+ *envp = *(envp + rtld_argc);
+ } while (*envp++ != NULL);
+ aux = auxp = (Elf_Auxinfo *)envp;
+ auxpf = (Elf_Auxinfo *)(envp + rtld_argc);
+ dbg("move aux from %p to %p", auxpf, aux);
+ /*
+ * XXXKIB insert place for AT_EXECPATH if not
+ * present
+ */
+ for (;; auxp++, auxpf++) {
+ /*
+ * NB: Use a temporary since *auxpf and
+ * *auxp overlap if rtld_argc is 1
+ */
+ auxtmp = *auxpf;
+ *auxp = auxtmp;
+ if (auxp->a_type == AT_NULL)
+ break;
+ }
+ /*
+ * Since the auxiliary vector has moved,
+ * redigest it.
+ */
+ for (i = 0; i < AT_COUNT; i++)
+ aux_info[i] = NULL;
+ for (auxp = aux; auxp->a_type != AT_NULL;
+ auxp++) {
+ if (auxp->a_type < AT_COUNT)
+ aux_info[auxp->a_type] = auxp;
+ }
+
+ /*
+ * Point AT_EXECPATH auxv and aux_info to the
+ * binary path.
+ */
+ if (binpath == NULL) {
+ aux_info[AT_EXECPATH] = NULL;
+ } else {
+ if (aux_info[AT_EXECPATH] == NULL) {
+ aux_info[AT_EXECPATH] = xmalloc(
+ sizeof(Elf_Auxinfo));
+ aux_info[AT_EXECPATH]->a_type =
+ AT_EXECPATH;
+ }
+ aux_info[AT_EXECPATH]->a_un.a_ptr =
+ __DECONST(void *, binpath);
+ }
+ } else {
+ _rtld_error("No binary");
+ rtld_die();
+ }
+ }
+ }
+
+ ld_bind_now = ld_get_env_var(LD_BIND_NOW);
+
+ /*
+ * If the process is tainted, then we un-set the dangerous environment
+ * variables. The process will be marked as tainted until setuid(2)
+ * is called. If any child process calls setuid(2) we do not want any
+ * future processes to honor the potentially un-safe variables.
+ */
+ if (!trust) {
+ for (i = 0; i < (int)nitems(ld_env_vars); i++) {
+ lvd = &ld_env_vars[i];
+ if (lvd->unsecure)
+ lvd->val = NULL;
+ }
+ }
+
+ ld_debug = ld_get_env_var(LD_DEBUG);
+ if (ld_bind_now == NULL)
+ ld_bind_not = ld_get_env_var(LD_BIND_NOT) != NULL;
+ ld_dynamic_weak = ld_get_env_var(LD_DYNAMIC_WEAK) == NULL;
+ libmap_disable = ld_get_env_var(LD_LIBMAP_DISABLE) != NULL;
+ libmap_override = ld_get_env_var(LD_LIBMAP);
+ ld_library_path = ld_get_env_var(LD_LIBRARY_PATH);
+ ld_library_dirs = ld_get_env_var(LD_LIBRARY_PATH_FDS);
+ ld_preload = ld_get_env_var(LD_PRELOAD);
+ ld_preload_fds = ld_get_env_var(LD_PRELOAD_FDS);
+ ld_elf_hints_path = ld_get_env_var(LD_ELF_HINTS_PATH);
+ ld_loadfltr = ld_get_env_var(LD_LOADFLTR) != NULL;
+ library_path_rpath = ld_get_env_var(LD_LIBRARY_PATH_RPATH);
+ if (library_path_rpath != NULL) {
+ if (library_path_rpath[0] == 'y' ||
+ library_path_rpath[0] == 'Y' ||
+ library_path_rpath[0] == '1')
+ ld_library_path_rpath = true;
+ else
+ ld_library_path_rpath = false;
+ }
+ static_tls_extra = ld_get_env_var(LD_STATIC_TLS_EXTRA);
+ if (static_tls_extra != NULL && static_tls_extra[0] != '\0') {
+ sz = parse_integer(static_tls_extra);
+ if (sz >= RTLD_STATIC_TLS_EXTRA && sz <= SIZE_T_MAX)
+ ld_static_tls_extra = sz;
+ }
+ dangerous_ld_env = libmap_disable || libmap_override != NULL ||
+ ld_library_path != NULL || ld_preload != NULL ||
+ ld_elf_hints_path != NULL || ld_loadfltr || !ld_dynamic_weak ||
+ static_tls_extra != NULL;
+ ld_tracing = ld_get_env_var(LD_TRACE_LOADED_OBJECTS);
+ ld_utrace = ld_get_env_var(LD_UTRACE);
+
+ set_ld_elf_hints_path();
+ if (ld_debug != NULL && *ld_debug != '\0')
+ debug = 1;
+ dbg("%s is initialized, base address = %p", __progname,
+ (caddr_t)aux_info[AT_BASE]->a_un.a_ptr);
+ dbg("RTLD dynamic = %p", obj_rtld.dynamic);
+ dbg("RTLD pltgot = %p", obj_rtld.pltgot);
+
+ dbg("initializing thread locks");
+ lockdflt_init();
+
+ /*
+ * Load the main program, or process its program header if it is
+ * already loaded.
+ */
+ if (fd != -1) { /* Load the main program. */
+ dbg("loading main program");
+ obj_main = map_object(fd, argv0, NULL, true);
+ close(fd);
+ if (obj_main == NULL)
+ rtld_die();
+ max_stack_flags = obj_main->stack_flags;
+ } else { /* Main program already loaded. */
+ dbg("processing main program's program header");
+ assert(aux_info[AT_PHDR] != NULL);
+ phdr = (const Elf_Phdr *)aux_info[AT_PHDR]->a_un.a_ptr;
+ assert(aux_info[AT_PHNUM] != NULL);
+ phnum = aux_info[AT_PHNUM]->a_un.a_val;
+ assert(aux_info[AT_PHENT] != NULL);
+ assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr));
+ assert(aux_info[AT_ENTRY] != NULL);
+ imgentry = (caddr_t)aux_info[AT_ENTRY]->a_un.a_ptr;
+ if ((obj_main = digest_phdr(phdr, phnum, imgentry, argv0)) ==
+ NULL)
+ rtld_die();
+ }
+
+ if (aux_info[AT_EXECPATH] != NULL && fd == -1) {
+ kexecpath = aux_info[AT_EXECPATH]->a_un.a_ptr;
+ dbg("AT_EXECPATH %p %s", kexecpath, kexecpath);
+ if (kexecpath[0] == '/')
+ obj_main->path = kexecpath;
+ else if (getcwd(buf, sizeof(buf)) == NULL ||
+ strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) ||
+ strlcat(buf, kexecpath, sizeof(buf)) >= sizeof(buf))
+ obj_main->path = xstrdup(argv0);
+ else
+ obj_main->path = xstrdup(buf);
+ } else {
+ dbg("No AT_EXECPATH or direct exec");
+ obj_main->path = xstrdup(argv0);
+ }
+ dbg("obj_main path %s", obj_main->path);
+ obj_main->mainprog = true;
+
+ if (aux_info[AT_STACKPROT] != NULL &&
+ aux_info[AT_STACKPROT]->a_un.a_val != 0)
+ stack_prot = aux_info[AT_STACKPROT]->a_un.a_val;
+
+#ifndef COMPAT_libcompat
+ /*
+ * Get the actual dynamic linker pathname from the executable if
+ * possible. (It should always be possible.) That ensures that
+ * gdb will find the right dynamic linker even if a non-standard
+ * one is being used.
+ */
+ if (obj_main->interp != NULL &&
+ strcmp(obj_main->interp, obj_rtld.path) != 0) {
+ free(obj_rtld.path);
+ obj_rtld.path = xstrdup(obj_main->interp);
+ __progname = obj_rtld.path;
+ }
+#endif
+
+ if (!digest_dynamic(obj_main, 0))
+ rtld_die();
+ dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d",
+ obj_main->path, obj_main->valid_hash_sysv, obj_main->valid_hash_gnu,
+ obj_main->dynsymcount);
+
+ linkmap_add(obj_main);
+ linkmap_add(&obj_rtld);
+ LD_UTRACE(UTRACE_LOAD_OBJECT, obj_main, obj_main->mapbase,
+ obj_main->mapsize, 0, obj_main->path);
+ LD_UTRACE(UTRACE_LOAD_OBJECT, &obj_rtld, obj_rtld.mapbase,
+ obj_rtld.mapsize, 0, obj_rtld.path);
+
+ /* Link the main program into the list of objects. */
+ TAILQ_INSERT_HEAD(&obj_list, obj_main, next);
+ obj_count++;
+ obj_loads++;
+
+ /* Initialize a fake symbol for resolving undefined weak references. */
+ sym_zero.st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE);
+ sym_zero.st_shndx = SHN_UNDEF;
+ sym_zero.st_value = -(uintptr_t)obj_main->relocbase;
+
+ if (!libmap_disable)
+ libmap_disable = (bool)lm_init(libmap_override);
+
+ if (aux_info[AT_KPRELOAD] != NULL &&
+ aux_info[AT_KPRELOAD]->a_un.a_ptr != NULL) {
+ dbg("loading kernel vdso");
+ if (load_kpreload(aux_info[AT_KPRELOAD]->a_un.a_ptr) == -1)
+ rtld_die();
+ }
+
+ dbg("loading LD_PRELOAD_FDS libraries");
+ if (load_preload_objects(ld_preload_fds, true) == -1)
+ rtld_die();
+
+ dbg("loading LD_PRELOAD libraries");
+ if (load_preload_objects(ld_preload, false) == -1)
+ rtld_die();
+ preload_tail = globallist_curr(TAILQ_LAST(&obj_list, obj_entry_q));
+
+ dbg("loading needed objects");
+ if (load_needed_objects(obj_main,
+ ld_tracing != NULL ? RTLD_LO_TRACE : 0) == -1)
+ rtld_die();
+
+ /* Make a list of all objects loaded at startup. */
+ last_interposer = obj_main;
+ TAILQ_FOREACH(obj, &obj_list, next) {
+ if (obj->marker)
+ continue;
+ if (obj->z_interpose && obj != obj_main) {
+ objlist_put_after(&list_main, last_interposer, obj);
+ last_interposer = obj;
+ } else {
+ objlist_push_tail(&list_main, obj);
+ }
+ obj->refcount++;
+ }
+
+ dbg("checking for required versions");
+ if (rtld_verify_versions(&list_main) == -1 && !ld_tracing)
+ rtld_die();
+
+ if (ld_get_env_var(LD_SHOW_AUXV) != NULL)
+ dump_auxv(aux_info);
+
+ if (ld_tracing) { /* We're done */
+ trace_loaded_objects(obj_main, true);
+ exit(0);
+ }
+
+ if (ld_get_env_var(LD_DUMP_REL_PRE) != NULL) {
+ dump_relocations(obj_main);
+ exit(0);
+ }
+
+ /*
+ * Processing tls relocations requires having the tls offsets
+ * initialized. Prepare offsets before starting initial
+ * relocation processing.
+ */
+ dbg("initializing initial thread local storage offsets");
+ STAILQ_FOREACH(entry, &list_main, link) {
+ /*
+ * Allocate all the initial objects out of the static TLS
+ * block even if they didn't ask for it.
+ */
+ allocate_tls_offset(entry->obj);
+ }
+
+ if (!allocate_tls_offset_common(&tcb_list_entry_offset,
+ sizeof(struct tcb_list_entry), _Alignof(struct tcb_list_entry),
+ 0)) {
+ /*
+ * This should be impossible as the static block size is not
+ * yet fixed, but catch and diagnose it failing if that ever
+ * changes or somehow turns out to be false.
+ */
+ _rtld_error("Could not allocate offset for tcb_list_entry");
+ rtld_die();
+ }
+ dbg("tcb_list_entry_offset %zu", tcb_list_entry_offset);
+
+ if (relocate_objects(obj_main,
+ ld_bind_now != NULL && *ld_bind_now != '\0', &obj_rtld,
+ SYMLOOK_EARLY, NULL) == -1)
+ rtld_die();
+
+ dbg("doing copy relocations");
+ if (do_copy_relocations(obj_main) == -1)
+ rtld_die();
+
+ if (ld_get_env_var(LD_DUMP_REL_POST) != NULL) {
+ dump_relocations(obj_main);
+ exit(0);
+ }
+
+ ifunc_init(aux_info);
+
+ /*
+ * Setup TLS for main thread. This must be done after the
+ * relocations are processed, since tls initialization section
+ * might be the subject for relocations.
+ */
+ dbg("initializing initial thread local storage");
+ allocate_initial_tls(globallist_curr(TAILQ_FIRST(&obj_list)));
+
+ dbg("initializing key program variables");
+ set_program_var("__progname", argv[0] != NULL ? basename(argv[0]) : "");
+ set_program_var("environ", env);
+ set_program_var("__elf_aux_vector", aux);
+
+ /* Make a list of init functions to call. */
+ objlist_init(&initlist);
+ initlist_for_loaded_obj(globallist_curr(TAILQ_FIRST(&obj_list)),
+ preload_tail, &initlist);
+
+ r_debug_state(NULL, &obj_main->linkmap); /* say hello to gdb! */
+
+ map_stacks_exec(NULL);
+
+ if (!obj_main->crt_no_init) {
+ /*
+ * Make sure we don't call the main program's init and fini
+ * functions for binaries linked with old crt1 which calls
+ * _init itself.
+ */
+ obj_main->init = obj_main->fini = (Elf_Addr)NULL;
+ obj_main->preinit_array = obj_main->init_array =
+ obj_main->fini_array = (Elf_Addr)NULL;
+ }
+
+ if (direct_exec) {
+ /* Set osrel for direct-execed binary */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_OSREL;
+ mib[3] = getpid();
+ osrel = obj_main->osrel;
+ sz = sizeof(old_osrel);
+ dbg("setting osrel to %d", osrel);
+ (void)sysctl(mib, 4, &old_osrel, &sz, &osrel, sizeof(osrel));
+ }
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+
+ dbg("resolving ifuncs");
+ if (initlist_objects_ifunc(&initlist,
+ ld_bind_now != NULL && *ld_bind_now != '\0', SYMLOOK_EARLY,
+ &lockstate) == -1)
+ rtld_die();
+
+ rtld_exit_ptr = rtld_exit;
+ if (obj_main->crt_no_init)
+ preinit_main();
+ objlist_call_init(&initlist, &lockstate);
+ _r_debug_postinit(&obj_main->linkmap);
+ objlist_clear(&initlist);
+ dbg("loading filtees");
+ TAILQ_FOREACH(obj, &obj_list, next) {
+ if (obj->marker)
+ continue;
+ if (ld_loadfltr || obj->z_loadfltr)
+ load_filtees(obj, 0, &lockstate);
+ }
+
+ dbg("enforcing main obj relro");
+ if (obj_enforce_relro(obj_main) == -1)
+ rtld_die();
+
+ lock_release(rtld_bind_lock, &lockstate);
+
+ dbg("transferring control to program entry point = %p",
+ obj_main->entry);
+
+ /* Return the exit procedure and the program entry point. */
+ *exit_proc = rtld_exit_ptr;
+ *objp = obj_main;
+ return ((func_ptr_type)obj_main->entry);
+}
+
+void *
+rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
+{
+ void *ptr;
+ Elf_Addr target;
+
+ ptr = (void *)make_function_pointer(def, obj);
+ target = call_ifunc_resolver(ptr);
+ return ((void *)target);
+}
+
+Elf_Addr
+_rtld_bind(Obj_Entry *obj, Elf_Size reloff)
+{
+ const Elf_Rel *rel;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr *where;
+ Elf_Addr target;
+ RtldLockState lockstate;
+
+relock:
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ if (sigsetjmp(lockstate.env, 0) != 0)
+ lock_upgrade(rtld_bind_lock, &lockstate);
+ if (obj->pltrel)
+ rel = (const Elf_Rel *)((const char *)obj->pltrel + reloff);
+ else
+ rel = (const Elf_Rel *)((const char *)obj->pltrela + reloff);
+
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT,
+ NULL, &lockstate);
+ if (def == NULL)
+ rtld_die();
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ if (lockstate_wlocked(&lockstate)) {
+ lock_release(rtld_bind_lock, &lockstate);
+ goto relock;
+ }
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ } else {
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ }
+
+ dbg("\"%s\" in \"%s\" ==> %p in \"%s\"", defobj->strtab + def->st_name,
+ obj->path == NULL ? NULL : basename(obj->path), (void *)target,
+ defobj->path == NULL ? NULL : basename(defobj->path));
+
+ /*
+ * Write the new contents for the jmpslot. Note that depending on
+ * architecture, the value which we need to return back to the
+ * lazy binding trampoline may or may not be the target
+ * address. The value returned from reloc_jmpslot() is the value
+ * that the trampoline needs.
+ */
+ target = reloc_jmpslot(where, target, defobj, obj, rel);
+ lock_release(rtld_bind_lock, &lockstate);
+ return (target);
+}
+
+/*
+ * Error reporting function. Use it like printf. If formats the message
+ * into a buffer, and sets things up so that the next call to dlerror()
+ * will return the message.
+ */
+void
+_rtld_error(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ rtld_vsnprintf(lockinfo.dlerror_loc(), lockinfo.dlerror_loc_sz, fmt,
+ ap);
+ va_end(ap);
+ *lockinfo.dlerror_seen() = 0;
+ dbg("rtld_error: %s", lockinfo.dlerror_loc());
+ LD_UTRACE(UTRACE_RTLD_ERROR, NULL, NULL, 0, 0, lockinfo.dlerror_loc());
+}
+
+/*
+ * Return a dynamically-allocated copy of the current error message, if any.
+ */
+static struct dlerror_save *
+errmsg_save(void)
+{
+ struct dlerror_save *res;
+
+ res = xmalloc(sizeof(*res));
+ res->seen = *lockinfo.dlerror_seen();
+ if (res->seen == 0)
+ res->msg = xstrdup(lockinfo.dlerror_loc());
+ return (res);
+}
+
+/*
+ * Restore the current error message from a copy which was previously saved
+ * by errmsg_save(). The copy is freed.
+ */
+static void
+errmsg_restore(struct dlerror_save *saved_msg)
+{
+ if (saved_msg == NULL || saved_msg->seen == 1) {
+ *lockinfo.dlerror_seen() = 1;
+ } else {
+ *lockinfo.dlerror_seen() = 0;
+ strlcpy(lockinfo.dlerror_loc(), saved_msg->msg,
+ lockinfo.dlerror_loc_sz);
+ free(saved_msg->msg);
+ }
+ free(saved_msg);
+}
+
+static const char *
+basename(const char *name)
+{
+ const char *p;
+
+ p = strrchr(name, '/');
+ return (p != NULL ? p + 1 : name);
+}
+
+static struct utsname uts;
+
+static char *
+origin_subst_one(Obj_Entry *obj, char *real, const char *kw, const char *subst,
+ bool may_free)
+{
+ char *p, *p1, *res, *resp;
+ int subst_len, kw_len, subst_count, old_len, new_len;
+
+ kw_len = strlen(kw);
+
+ /*
+ * First, count the number of the keyword occurrences, to
+ * preallocate the final string.
+ */
+ for (p = real, subst_count = 0;; p = p1 + kw_len, subst_count++) {
+ p1 = strstr(p, kw);
+ if (p1 == NULL)
+ break;
+ }
+
+ /*
+ * If the keyword is not found, just return.
+ *
+ * Return non-substituted string if resolution failed. We
+ * cannot do anything more reasonable, the failure mode of the
+ * caller is unresolved library anyway.
+ */
+ if (subst_count == 0 || (obj != NULL && !obj_resolve_origin(obj)))
+ return (may_free ? real : xstrdup(real));
+ if (obj != NULL)
+ subst = obj->origin_path;
+
+ /*
+ * There is indeed something to substitute. Calculate the
+ * length of the resulting string, and allocate it.
+ */
+ subst_len = strlen(subst);
+ old_len = strlen(real);
+ new_len = old_len + (subst_len - kw_len) * subst_count;
+ res = xmalloc(new_len + 1);
+
+ /*
+ * Now, execute the substitution loop.
+ */
+ for (p = real, resp = res, *resp = '\0';;) {
+ p1 = strstr(p, kw);
+ if (p1 != NULL) {
+ /* Copy the prefix before keyword. */
+ memcpy(resp, p, p1 - p);
+ resp += p1 - p;
+ /* Keyword replacement. */
+ memcpy(resp, subst, subst_len);
+ resp += subst_len;
+ *resp = '\0';
+ p = p1 + kw_len;
+ } else
+ break;
+ }
+
+ /* Copy to the end of string and finish. */
+ strcat(resp, p);
+ if (may_free)
+ free(real);
+ return (res);
+}
+
+static const struct {
+ const char *kw;
+ bool pass_obj;
+ const char *subst;
+} tokens[] = {
+ { .kw = "$ORIGIN", .pass_obj = true, .subst = NULL },
+ { .kw = "${ORIGIN}", .pass_obj = true, .subst = NULL },
+ { .kw = "$OSNAME", .pass_obj = false, .subst = uts.sysname },
+ { .kw = "${OSNAME}", .pass_obj = false, .subst = uts.sysname },
+ { .kw = "$OSREL", .pass_obj = false, .subst = uts.release },
+ { .kw = "${OSREL}", .pass_obj = false, .subst = uts.release },
+ { .kw = "$PLATFORM", .pass_obj = false, .subst = uts.machine },
+ { .kw = "${PLATFORM}", .pass_obj = false, .subst = uts.machine },
+ { .kw = "$LIB", .pass_obj = false, .subst = TOKEN_LIB },
+ { .kw = "${LIB}", .pass_obj = false, .subst = TOKEN_LIB },
+};
+
+static char *
+origin_subst(Obj_Entry *obj, const char *real)
+{
+ char *res;
+ int i;
+
+ if (obj == NULL || !trust)
+ return (xstrdup(real));
+ if (uts.sysname[0] == '\0') {
+ if (uname(&uts) != 0) {
+ _rtld_error("utsname failed: %d", errno);
+ return (NULL);
+ }
+ }
+
+ /* __DECONST is safe here since without may_free real is unchanged */
+ res = __DECONST(char *, real);
+ for (i = 0; i < (int)nitems(tokens); i++) {
+ res = origin_subst_one(tokens[i].pass_obj ? obj : NULL, res,
+ tokens[i].kw, tokens[i].subst, i != 0);
+ }
+ return (res);
+}
+
+void
+rtld_die(void)
+{
+ const char *msg = dlerror();
+
+ if (msg == NULL)
+ msg = "Fatal error";
+ rtld_fdputstr(STDERR_FILENO, _BASENAME_RTLD ": ");
+ rtld_fdputstr(STDERR_FILENO, msg);
+ rtld_fdputchar(STDERR_FILENO, '\n');
+ _exit(1);
+}
+
+/*
+ * Process a shared object's DYNAMIC section, and save the important
+ * information in its Obj_Entry structure.
+ */
+static void
+digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
+ const Elf_Dyn **dyn_soname, const Elf_Dyn **dyn_runpath)
+{
+ const Elf_Dyn *dynp;
+ Needed_Entry **needed_tail = &obj->needed;
+ Needed_Entry **needed_filtees_tail = &obj->needed_filtees;
+ Needed_Entry **needed_aux_filtees_tail = &obj->needed_aux_filtees;
+ const Elf_Hashelt *hashtab;
+ const Elf32_Word *hashval;
+ Elf32_Word bkt, nmaskwords;
+ int bloom_size32;
+ int plttype = DT_REL;
+
+ *dyn_rpath = NULL;
+ *dyn_soname = NULL;
+ *dyn_runpath = NULL;
+
+ obj->bind_now = false;
+ dynp = obj->dynamic;
+ if (dynp == NULL)
+ return;
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_REL:
+ obj->rel = (const Elf_Rel *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_RELSZ:
+ obj->relsize = dynp->d_un.d_val;
+ break;
+
+ case DT_RELENT:
+ assert(dynp->d_un.d_val == sizeof(Elf_Rel));
+ break;
+
+ case DT_JMPREL:
+ obj->pltrel = (const Elf_Rel *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_PLTRELSZ:
+ obj->pltrelsize = dynp->d_un.d_val;
+ break;
+
+ case DT_RELA:
+ obj->rela = (const Elf_Rela *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_RELASZ:
+ obj->relasize = dynp->d_un.d_val;
+ break;
+
+ case DT_RELAENT:
+ assert(dynp->d_un.d_val == sizeof(Elf_Rela));
+ break;
+
+ case DT_RELR:
+ obj->relr = (const Elf_Relr *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_RELRSZ:
+ obj->relrsize = dynp->d_un.d_val;
+ break;
+
+ case DT_RELRENT:
+ assert(dynp->d_un.d_val == sizeof(Elf_Relr));
+ break;
+
+ case DT_PLTREL:
+ plttype = dynp->d_un.d_val;
+ assert(
+ dynp->d_un.d_val == DT_REL || plttype == DT_RELA);
+ break;
+
+ case DT_SYMTAB:
+ obj->symtab = (const Elf_Sym *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_SYMENT:
+ assert(dynp->d_un.d_val == sizeof(Elf_Sym));
+ break;
+
+ case DT_STRTAB:
+ obj->strtab = (const char *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_STRSZ:
+ obj->strsize = dynp->d_un.d_val;
+ break;
+
+ case DT_VERNEED:
+ obj->verneed = (const Elf_Verneed *)(obj->relocbase +
+ dynp->d_un.d_val);
+ break;
+
+ case DT_VERNEEDNUM:
+ obj->verneednum = dynp->d_un.d_val;
+ break;
+
+ case DT_VERDEF:
+ obj->verdef = (const Elf_Verdef *)(obj->relocbase +
+ dynp->d_un.d_val);
+ break;
+
+ case DT_VERDEFNUM:
+ obj->verdefnum = dynp->d_un.d_val;
+ break;
+
+ case DT_VERSYM:
+ obj->versyms = (const Elf_Versym *)(obj->relocbase +
+ dynp->d_un.d_val);
+ break;
+
+ case DT_HASH: {
+ hashtab = (const Elf_Hashelt *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ obj->nbuckets = hashtab[0];
+ obj->nchains = hashtab[1];
+ obj->buckets = hashtab + 2;
+ obj->chains = obj->buckets + obj->nbuckets;
+ obj->valid_hash_sysv = obj->nbuckets > 0 &&
+ obj->nchains > 0 && obj->buckets != NULL;
+ } break;
+
+ case DT_GNU_HASH: {
+ hashtab = (const Elf_Hashelt *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ obj->nbuckets_gnu = hashtab[0];
+ obj->symndx_gnu = hashtab[1];
+ nmaskwords = hashtab[2];
+ bloom_size32 = (__ELF_WORD_SIZE / 32) * nmaskwords;
+ obj->maskwords_bm_gnu = nmaskwords - 1;
+ obj->shift2_gnu = hashtab[3];
+ obj->bloom_gnu = (const Elf_Addr *)(hashtab + 4);
+ obj->buckets_gnu = hashtab + 4 + bloom_size32;
+ obj->chain_zero_gnu = obj->buckets_gnu +
+ obj->nbuckets_gnu - obj->symndx_gnu;
+ /* Number of bitmask words is required to be power of 2
+ */
+ obj->valid_hash_gnu = powerof2(nmaskwords) &&
+ obj->nbuckets_gnu > 0 && obj->buckets_gnu != NULL;
+ } break;
+
+ case DT_NEEDED:
+ if (!obj->rtld) {
+ Needed_Entry *nep = NEW(Needed_Entry);
+ nep->name = dynp->d_un.d_val;
+ nep->obj = NULL;
+ nep->next = NULL;
+
+ *needed_tail = nep;
+ needed_tail = &nep->next;
+ }
+ break;
+
+ case DT_FILTER:
+ if (!obj->rtld) {
+ Needed_Entry *nep = NEW(Needed_Entry);
+ nep->name = dynp->d_un.d_val;
+ nep->obj = NULL;
+ nep->next = NULL;
+
+ *needed_filtees_tail = nep;
+ needed_filtees_tail = &nep->next;
+
+ if (obj->linkmap.l_refname == NULL)
+ obj->linkmap.l_refname =
+ (char *)dynp->d_un.d_val;
+ }
+ break;
+
+ case DT_AUXILIARY:
+ if (!obj->rtld) {
+ Needed_Entry *nep = NEW(Needed_Entry);
+ nep->name = dynp->d_un.d_val;
+ nep->obj = NULL;
+ nep->next = NULL;
+
+ *needed_aux_filtees_tail = nep;
+ needed_aux_filtees_tail = &nep->next;
+ }
+ break;
+
+ case DT_PLTGOT:
+ obj->pltgot = (Elf_Addr *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_TEXTREL:
+ obj->textrel = true;
+ break;
+
+ case DT_SYMBOLIC:
+ obj->symbolic = true;
+ break;
+
+ case DT_RPATH:
+ /*
+ * We have to wait until later to process this, because
+ * we might not have gotten the address of the string
+ * table yet.
+ */
+ *dyn_rpath = dynp;
+ break;
+
+ case DT_SONAME:
+ *dyn_soname = dynp;
+ break;
+
+ case DT_RUNPATH:
+ *dyn_runpath = dynp;
+ break;
+
+ case DT_INIT:
+ obj->init = (Elf_Addr)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_PREINIT_ARRAY:
+ obj->preinit_array = (Elf_Addr)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_PREINIT_ARRAYSZ:
+ obj->preinit_array_num = dynp->d_un.d_val /
+ sizeof(Elf_Addr);
+ break;
+
+ case DT_INIT_ARRAY:
+ obj->init_array = (Elf_Addr)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_INIT_ARRAYSZ:
+ obj->init_array_num = dynp->d_un.d_val /
+ sizeof(Elf_Addr);
+ break;
+
+ case DT_FINI:
+ obj->fini = (Elf_Addr)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_FINI_ARRAY:
+ obj->fini_array = (Elf_Addr)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
+ case DT_FINI_ARRAYSZ:
+ obj->fini_array_num = dynp->d_un.d_val /
+ sizeof(Elf_Addr);
+ break;
+
+ case DT_DEBUG:
+ if (!early)
+ dbg("Filling in DT_DEBUG entry");
+ (__DECONST(Elf_Dyn *, dynp))->d_un.d_ptr =
+ (Elf_Addr)&r_debug;
+ break;
+
+ case DT_FLAGS:
+ if (dynp->d_un.d_val & DF_ORIGIN)
+ obj->z_origin = true;
+ if (dynp->d_un.d_val & DF_SYMBOLIC)
+ obj->symbolic = true;
+ if (dynp->d_un.d_val & DF_TEXTREL)
+ obj->textrel = true;
+ if (dynp->d_un.d_val & DF_BIND_NOW)
+ obj->bind_now = true;
+ if (dynp->d_un.d_val & DF_STATIC_TLS)
+ obj->static_tls = true;
+ break;
+
+ case DT_FLAGS_1:
+ if (dynp->d_un.d_val & DF_1_NOOPEN)
+ obj->z_noopen = true;
+ if (dynp->d_un.d_val & DF_1_ORIGIN)
+ obj->z_origin = true;
+ if (dynp->d_un.d_val & DF_1_GLOBAL)
+ obj->z_global = true;
+ if (dynp->d_un.d_val & DF_1_BIND_NOW)
+ obj->bind_now = true;
+ if (dynp->d_un.d_val & DF_1_NODELETE)
+ obj->z_nodelete = true;
+ if (dynp->d_un.d_val & DF_1_LOADFLTR)
+ obj->z_loadfltr = true;
+ if (dynp->d_un.d_val & DF_1_INTERPOSE)
+ obj->z_interpose = true;
+ if (dynp->d_un.d_val & DF_1_NODEFLIB)
+ obj->z_nodeflib = true;
+ if (dynp->d_un.d_val & DF_1_PIE)
+ obj->z_pie = true;
+ if (dynp->d_un.d_val & DF_1_INITFIRST)
+ obj->z_initfirst = true;
+ break;
+
+ default:
+ if (arch_digest_dynamic(obj, dynp))
+ break;
+
+ if (!early) {
+ dbg("Ignoring d_tag %ld = %#lx",
+ (long)dynp->d_tag, (long)dynp->d_tag);
+ }
+ break;
+ }
+ }
+
+ obj->traced = false;
+
+ if (plttype == DT_RELA) {
+ obj->pltrela = (const Elf_Rela *)obj->pltrel;
+ obj->pltrel = NULL;
+ obj->pltrelasize = obj->pltrelsize;
+ obj->pltrelsize = 0;
+ }
+
+ /* Determine size of dynsym table (equal to nchains of sysv hash) */
+ if (obj->valid_hash_sysv)
+ obj->dynsymcount = obj->nchains;
+ else if (obj->valid_hash_gnu) {
+ obj->dynsymcount = 0;
+ for (bkt = 0; bkt < obj->nbuckets_gnu; bkt++) {
+ if (obj->buckets_gnu[bkt] == 0)
+ continue;
+ hashval = &obj->chain_zero_gnu[obj->buckets_gnu[bkt]];
+ do
+ obj->dynsymcount++;
+ while ((*hashval++ & 1u) == 0);
+ }
+ obj->dynsymcount += obj->symndx_gnu;
+ }
+
+ if (obj->linkmap.l_refname != NULL)
+ obj->linkmap.l_refname = obj->strtab +
+ (unsigned long)obj->linkmap.l_refname;
+}
+
+static bool
+obj_resolve_origin(Obj_Entry *obj)
+{
+ if (obj->origin_path != NULL)
+ return (true);
+ obj->origin_path = xmalloc(PATH_MAX);
+ return (rtld_dirname_abs(obj->path, obj->origin_path) != -1);
+}
+
+static bool
+digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath,
+ const Elf_Dyn *dyn_soname, const Elf_Dyn *dyn_runpath)
+{
+ if (obj->z_origin && !obj_resolve_origin(obj))
+ return (false);
+
+ if (dyn_runpath != NULL) {
+ obj->runpath = (const char *)obj->strtab +
+ dyn_runpath->d_un.d_val;
+ obj->runpath = origin_subst(obj, obj->runpath);
+ } else if (dyn_rpath != NULL) {
+ obj->rpath = (const char *)obj->strtab + dyn_rpath->d_un.d_val;
+ obj->rpath = origin_subst(obj, obj->rpath);
+ }
+ if (dyn_soname != NULL)
+ object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val);
+ return (true);
+}
+
+static bool
+digest_dynamic(Obj_Entry *obj, int early)
+{
+ const Elf_Dyn *dyn_rpath;
+ const Elf_Dyn *dyn_soname;
+ const Elf_Dyn *dyn_runpath;
+
+ digest_dynamic1(obj, early, &dyn_rpath, &dyn_soname, &dyn_runpath);
+ return (digest_dynamic2(obj, dyn_rpath, dyn_soname, dyn_runpath));
+}
+
+/*
+ * Process a shared object's program header. This is used only for the
+ * main program, when the kernel has already loaded the main program
+ * into memory before calling the dynamic linker. It creates and
+ * returns an Obj_Entry structure.
+ */
+static Obj_Entry *
+digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
+{
+ Obj_Entry *obj;
+ const Elf_Phdr *phlimit = phdr + phnum;
+ const Elf_Phdr *ph;
+ Elf_Addr note_start, note_end;
+ int nsegs = 0;
+
+ obj = obj_new();
+ for (ph = phdr; ph < phlimit; ph++) {
+ if (ph->p_type != PT_PHDR)
+ continue;
+
+ obj->phdr = phdr;
+ obj->phsize = ph->p_memsz;
+ obj->relocbase = __DECONST(char *, phdr) - ph->p_vaddr;
+ break;
+ }
+
+ obj->stack_flags = PF_X | PF_R | PF_W;
+
+ for (ph = phdr; ph < phlimit; ph++) {
+ switch (ph->p_type) {
+ case PT_INTERP:
+ obj->interp = (const char *)(ph->p_vaddr +
+ obj->relocbase);
+ break;
+
+ case PT_LOAD:
+ if (nsegs == 0) { /* First load segment */
+ obj->vaddrbase = rtld_trunc_page(ph->p_vaddr);
+ obj->mapbase = obj->vaddrbase + obj->relocbase;
+ } else { /* Last load segment */
+ obj->mapsize = rtld_round_page(
+ ph->p_vaddr + ph->p_memsz) -
+ obj->vaddrbase;
+ }
+ nsegs++;
+ break;
+
+ case PT_DYNAMIC:
+ obj->dynamic = (const Elf_Dyn *)(ph->p_vaddr +
+ obj->relocbase);
+ break;
+
+ case PT_TLS:
+ obj->tlsindex = 1;
+ obj->tlssize = ph->p_memsz;
+ obj->tlsalign = ph->p_align;
+ obj->tlsinitsize = ph->p_filesz;
+ obj->tlsinit = (void *)(ph->p_vaddr + obj->relocbase);
+ obj->tlspoffset = ph->p_offset;
+ break;
+
+ case PT_GNU_STACK:
+ obj->stack_flags = ph->p_flags;
+ break;
+
+ case PT_NOTE:
+ note_start = (Elf_Addr)obj->relocbase + ph->p_vaddr;
+ note_end = note_start + ph->p_filesz;
+ digest_notes(obj, note_start, note_end);
+ break;
+ }
+ }
+ if (nsegs < 1) {
+ _rtld_error("%s: too few PT_LOAD segments", path);
+ return (NULL);
+ }
+
+ obj->entry = entry;
+ return (obj);
+}
+
+void
+digest_notes(Obj_Entry *obj, Elf_Addr note_start, Elf_Addr note_end)
+{
+ const Elf_Note *note;
+ const char *note_name;
+ uintptr_t p;
+
+ for (note = (const Elf_Note *)note_start; (Elf_Addr)note < note_end;
+ note = (const Elf_Note *)((const char *)(note + 1) +
+ roundup2(note->n_namesz, sizeof(Elf32_Addr)) +
+ roundup2(note->n_descsz, sizeof(Elf32_Addr)))) {
+ if (arch_digest_note(obj, note))
+ continue;
+
+ if (note->n_namesz != sizeof(NOTE_FREEBSD_VENDOR) ||
+ note->n_descsz != sizeof(int32_t))
+ continue;
+ if (note->n_type != NT_FREEBSD_ABI_TAG &&
+ note->n_type != NT_FREEBSD_FEATURE_CTL &&
+ note->n_type != NT_FREEBSD_NOINIT_TAG)
+ continue;
+ note_name = (const char *)(note + 1);
+ if (strncmp(NOTE_FREEBSD_VENDOR, note_name,
+ sizeof(NOTE_FREEBSD_VENDOR)) != 0)
+ continue;
+ switch (note->n_type) {
+ case NT_FREEBSD_ABI_TAG:
+ /* FreeBSD osrel note */
+ p = (uintptr_t)(note + 1);
+ p += roundup2(note->n_namesz, sizeof(Elf32_Addr));
+ obj->osrel = *(const int32_t *)(p);
+ dbg("note osrel %d", obj->osrel);
+ break;
+ case NT_FREEBSD_FEATURE_CTL:
+ /* FreeBSD ABI feature control note */
+ p = (uintptr_t)(note + 1);
+ p += roundup2(note->n_namesz, sizeof(Elf32_Addr));
+ obj->fctl0 = *(const uint32_t *)(p);
+ dbg("note fctl0 %#x", obj->fctl0);
+ break;
+ case NT_FREEBSD_NOINIT_TAG:
+ /* FreeBSD 'crt does not call init' note */
+ obj->crt_no_init = true;
+ dbg("note crt_no_init");
+ break;
+ }
+ }
+}
+
+static Obj_Entry *
+dlcheck(void *handle)
+{
+ Obj_Entry *obj;
+
+ TAILQ_FOREACH(obj, &obj_list, next) {
+ if (obj == (Obj_Entry *)handle)
+ break;
+ }
+
+ if (obj == NULL || obj->refcount == 0 || obj->dl_refcount == 0) {
+ _rtld_error("Invalid shared object handle %p", handle);
+ return (NULL);
+ }
+ return (obj);
+}
+
+/*
+ * If the given object is already in the donelist, return true. Otherwise
+ * add the object to the list and return false.
+ */
+static bool
+donelist_check(DoneList *dlp, const Obj_Entry *obj)
+{
+ unsigned int i;
+
+ for (i = 0; i < dlp->num_used; i++)
+ if (dlp->objs[i] == obj)
+ return (true);
+ /*
+ * Our donelist allocation should always be sufficient. But if
+ * our threads locking isn't working properly, more shared objects
+ * could have been loaded since we allocated the list. That should
+ * never happen, but we'll handle it properly just in case it does.
+ */
+ if (dlp->num_used < dlp->num_alloc)
+ dlp->objs[dlp->num_used++] = obj;
+ return (false);
+}
+
+/*
+ * SysV hash function for symbol table lookup. It is a slightly optimized
+ * version of the hash specified by the System V ABI.
+ */
+Elf32_Word
+elf_hash(const char *name)
+{
+ const unsigned char *p = (const unsigned char *)name;
+ Elf32_Word h = 0;
+
+ while (*p != '\0') {
+ h = (h << 4) + *p++;
+ h ^= (h >> 24) & 0xf0;
+ }
+ return (h & 0x0fffffff);
+}
+
+/*
+ * The GNU hash function is the Daniel J. Bernstein hash clipped to 32 bits
+ * unsigned in case it's implemented with a wider type.
+ */
+static uint32_t
+gnu_hash(const char *s)
+{
+ uint32_t h;
+ unsigned char c;
+
+ h = 5381;
+ for (c = *s; c != '\0'; c = *++s)
+ h = h * 33 + c;
+ return (h & 0xffffffff);
+}
+
+/*
+ * Find the library with the given name, and return its full pathname.
+ * The returned string is dynamically allocated. Generates an error
+ * message and returns NULL if the library cannot be found.
+ *
+ * If the second argument is non-NULL, then it refers to an already-
+ * loaded shared object, whose library search path will be searched.
+ *
+ * If a library is successfully located via LD_LIBRARY_PATH_FDS, its
+ * descriptor (which is close-on-exec) will be passed out via the third
+ * argument.
+ *
+ * The search order is:
+ * DT_RPATH in the referencing file _unless_ DT_RUNPATH is present (1)
+ * DT_RPATH of the main object if DSO without defined DT_RUNPATH (1)
+ * LD_LIBRARY_PATH
+ * DT_RUNPATH in the referencing file
+ * ldconfig hints (if -z nodefaultlib, filter out default library directories
+ * from list)
+ * /lib:/usr/lib _unless_ the referencing file is linked with -z nodefaultlib
+ *
+ * (1) Handled in digest_dynamic2 - rpath left NULL if runpath defined.
+ */
+static char *
+find_library(const char *xname, const Obj_Entry *refobj, int *fdp)
+{
+ char *pathname, *refobj_path;
+ const char *name;
+ bool nodeflib, objgiven;
+
+ objgiven = refobj != NULL;
+
+ if (libmap_disable || !objgiven ||
+ (name = lm_find(refobj->path, xname)) == NULL)
+ name = xname;
+
+ if (strchr(name, '/') != NULL) { /* Hard coded pathname */
+ if (name[0] != '/' && !trust) {
+ _rtld_error(
+ "Absolute pathname required for shared object \"%s\"",
+ name);
+ return (NULL);
+ }
+ return (origin_subst(__DECONST(Obj_Entry *, refobj),
+ __DECONST(char *, name)));
+ }
+
+ dbg(" Searching for \"%s\"", name);
+ refobj_path = objgiven ? refobj->path : NULL;
+
+ /*
+ * If refobj->rpath != NULL, then refobj->runpath is NULL. Fall
+ * back to pre-conforming behaviour if user requested so with
+ * LD_LIBRARY_PATH_RPATH environment variable and ignore -z
+ * nodeflib.
+ */
+ if (objgiven && refobj->rpath != NULL && ld_library_path_rpath) {
+ pathname = search_library_path(name, ld_library_path,
+ refobj_path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ if (refobj != NULL) {
+ pathname = search_library_path(name, refobj->rpath,
+ refobj_path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ }
+ pathname = search_library_pathfds(name, ld_library_dirs, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ pathname = search_library_path(name, gethints(false),
+ refobj_path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ pathname = search_library_path(name, ld_standard_library_path,
+ refobj_path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ } else {
+ nodeflib = objgiven ? refobj->z_nodeflib : false;
+ if (objgiven) {
+ pathname = search_library_path(name, refobj->rpath,
+ refobj->path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ }
+ if (objgiven && refobj->runpath == NULL && refobj != obj_main) {
+ pathname = search_library_path(name, obj_main->rpath,
+ refobj_path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ }
+ pathname = search_library_path(name, ld_library_path,
+ refobj_path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ if (objgiven) {
+ pathname = search_library_path(name, refobj->runpath,
+ refobj_path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ }
+ pathname = search_library_pathfds(name, ld_library_dirs, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ pathname = search_library_path(name, gethints(nodeflib),
+ refobj_path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ if (objgiven && !nodeflib) {
+ pathname = search_library_path(name,
+ ld_standard_library_path, refobj_path, fdp);
+ if (pathname != NULL)
+ return (pathname);
+ }
+ }
+
+ if (objgiven && refobj->path != NULL) {
+ _rtld_error(
+ "Shared object \"%s\" not found, required by \"%s\"",
+ name, basename(refobj->path));
+ } else {
+ _rtld_error("Shared object \"%s\" not found", name);
+ }
+ return (NULL);
+}
+
+/*
+ * Given a symbol number in a referencing object, find the corresponding
+ * definition of the symbol. Returns a pointer to the symbol, or NULL if
+ * no definition was found. Returns a pointer to the Obj_Entry of the
+ * defining object via the reference parameter DEFOBJ_OUT.
+ */
+const Elf_Sym *
+find_symdef(unsigned long symnum, const Obj_Entry *refobj,
+ const Obj_Entry **defobj_out, int flags, SymCache *cache,
+ RtldLockState *lockstate)
+{
+ const Elf_Sym *ref;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ const Ver_Entry *ve;
+ SymLook req;
+ const char *name;
+ int res;
+
+ /*
+ * If we have already found this symbol, get the information from
+ * the cache.
+ */
+ if (symnum >= refobj->dynsymcount)
+ return (NULL); /* Bad object */
+ if (cache != NULL && cache[symnum].sym != NULL) {
+ *defobj_out = cache[symnum].obj;
+ return (cache[symnum].sym);
+ }
+
+ ref = refobj->symtab + symnum;
+ name = refobj->strtab + ref->st_name;
+ def = NULL;
+ defobj = NULL;
+ ve = NULL;
+
+ /*
+ * We don't have to do a full scale lookup if the symbol is local.
+ * We know it will bind to the instance in this load module; to
+ * which we already have a pointer (ie ref). By not doing a lookup,
+ * we not only improve performance, but it also avoids unresolvable
+ * symbols when local symbols are not in the hash table. This has
+ * been seen with the ia64 toolchain.
+ */
+ if (ELF_ST_BIND(ref->st_info) != STB_LOCAL) {
+ if (ELF_ST_TYPE(ref->st_info) == STT_SECTION) {
+ _rtld_error("%s: Bogus symbol table entry %lu",
+ refobj->path, symnum);
+ }
+ symlook_init(&req, name);
+ req.flags = flags;
+ ve = req.ventry = fetch_ventry(refobj, symnum);
+ req.lockstate = lockstate;
+ res = symlook_default(&req, refobj);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ } else {
+ def = ref;
+ defobj = refobj;
+ }
+
+ /*
+ * If we found no definition and the reference is weak, treat the
+ * symbol as having the value zero.
+ */
+ if (def == NULL && ELF_ST_BIND(ref->st_info) == STB_WEAK) {
+ def = &sym_zero;
+ defobj = obj_main;
+ }
+
+ if (def != NULL) {
+ *defobj_out = defobj;
+ /*
+ * Record the information in the cache to avoid subsequent
+ * lookups.
+ */
+ if (cache != NULL) {
+ cache[symnum].sym = def;
+ cache[symnum].obj = defobj;
+ }
+ } else {
+ if (refobj != &obj_rtld)
+ _rtld_error("%s: Undefined symbol \"%s%s%s\"",
+ refobj->path, name, ve != NULL ? "@" : "",
+ ve != NULL ? ve->name : "");
+ }
+ return (def);
+}
+
+/* Convert between native byte order and forced little resp. big endian. */
+#define COND_SWAP(n) (is_le ? le32toh(n) : be32toh(n))
+
+/*
+ * Return the search path from the ldconfig hints file, reading it if
+ * necessary. If nostdlib is true, then the default search paths are
+ * not added to result.
+ *
+ * Returns NULL if there are problems with the hints file,
+ * or if the search path there is empty.
+ */
+static const char *
+gethints(bool nostdlib)
+{
+ static char *filtered_path;
+ static const char *hints;
+ static struct elfhints_hdr hdr;
+ struct fill_search_info_args sargs, hargs;
+ struct dl_serinfo smeta, hmeta, *SLPinfo, *hintinfo;
+ struct dl_serpath *SLPpath, *hintpath;
+ char *p;
+ struct stat hint_stat;
+ unsigned int SLPndx, hintndx, fndx, fcount;
+ int fd;
+ size_t flen;
+ uint32_t dl;
+ uint32_t magic; /* Magic number */
+ uint32_t version; /* File version (1) */
+ uint32_t strtab; /* Offset of string table in file */
+ uint32_t dirlist; /* Offset of directory list in string table */
+ uint32_t dirlistlen; /* strlen(dirlist) */
+ bool is_le; /* Does the hints file use little endian */
+ bool skip;
+
+ /* First call, read the hints file */
+ if (hints == NULL) {
+ /* Keep from trying again in case the hints file is bad. */
+ hints = "";
+
+ if ((fd = open(ld_elf_hints_path, O_RDONLY | O_CLOEXEC)) ==
+ -1) {
+ dbg("failed to open hints file \"%s\"",
+ ld_elf_hints_path);
+ return (NULL);
+ }
+
+ /*
+ * Check of hdr.dirlistlen value against type limit
+ * intends to pacify static analyzers. Further
+ * paranoia leads to checks that dirlist is fully
+ * contained in the file range.
+ */
+ if (read(fd, &hdr, sizeof hdr) != sizeof hdr) {
+ dbg("failed to read %lu bytes from hints file \"%s\"",
+ (u_long)sizeof hdr, ld_elf_hints_path);
+cleanup1:
+ close(fd);
+ hdr.dirlistlen = 0;
+ return (NULL);
+ }
+ dbg("host byte-order: %s-endian",
+ le32toh(1) == 1 ? "little" : "big");
+ dbg("hints file byte-order: %s-endian",
+ hdr.magic == htole32(ELFHINTS_MAGIC) ? "little" : "big");
+ is_le = /*htole32(1) == 1 || */ hdr.magic ==
+ htole32(ELFHINTS_MAGIC);
+ magic = COND_SWAP(hdr.magic);
+ version = COND_SWAP(hdr.version);
+ strtab = COND_SWAP(hdr.strtab);
+ dirlist = COND_SWAP(hdr.dirlist);
+ dirlistlen = COND_SWAP(hdr.dirlistlen);
+ if (magic != ELFHINTS_MAGIC) {
+ dbg("invalid magic number %#08x (expected: %#08x)",
+ magic, ELFHINTS_MAGIC);
+ goto cleanup1;
+ }
+ if (version != 1) {
+ dbg("hints file version %d (expected: 1)", version);
+ goto cleanup1;
+ }
+ if (dirlistlen > UINT_MAX / 2) {
+ dbg("directory list is to long: %d > %d", dirlistlen,
+ UINT_MAX / 2);
+ goto cleanup1;
+ }
+ if (fstat(fd, &hint_stat) == -1) {
+ dbg("failed to find length of hints file \"%s\"",
+ ld_elf_hints_path);
+ goto cleanup1;
+ }
+ dl = strtab;
+ if (dl + dirlist < dl) {
+ dbg("invalid string table position %d", dl);
+ goto cleanup1;
+ }
+ dl += dirlist;
+ if (dl + dirlistlen < dl) {
+ dbg("invalid directory list offset %d", dirlist);
+ goto cleanup1;
+ }
+ dl += dirlistlen;
+ if (dl > hint_stat.st_size) {
+ dbg("hints file \"%s\" is truncated (%d vs. %jd bytes)",
+ ld_elf_hints_path, dl,
+ (uintmax_t)hint_stat.st_size);
+ goto cleanup1;
+ }
+ p = xmalloc(dirlistlen + 1);
+ if (pread(fd, p, dirlistlen + 1, strtab + dirlist) !=
+ (ssize_t)dirlistlen + 1 || p[dirlistlen] != '\0') {
+ free(p);
+ dbg(
+ "failed to read %d bytes starting at %d from hints file \"%s\"",
+ dirlistlen + 1, strtab + dirlist,
+ ld_elf_hints_path);
+ goto cleanup1;
+ }
+ hints = p;
+ close(fd);
+ }
+
+ /*
+ * If caller agreed to receive list which includes the default
+ * paths, we are done. Otherwise, if we still did not
+ * calculated filtered result, do it now.
+ */
+ if (!nostdlib)
+ return (hints[0] != '\0' ? hints : NULL);
+ if (filtered_path != NULL)
+ goto filt_ret;
+
+ /*
+ * Obtain the list of all configured search paths, and the
+ * list of the default paths.
+ *
+ * First estimate the size of the results.
+ */
+ smeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath);
+ smeta.dls_cnt = 0;
+ hmeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath);
+ hmeta.dls_cnt = 0;
+
+ sargs.request = RTLD_DI_SERINFOSIZE;
+ sargs.serinfo = &smeta;
+ hargs.request = RTLD_DI_SERINFOSIZE;
+ hargs.serinfo = &hmeta;
+
+ path_enumerate(ld_standard_library_path, fill_search_info, NULL,
+ &sargs);
+ path_enumerate(hints, fill_search_info, NULL, &hargs);
+
+ SLPinfo = xmalloc(smeta.dls_size);
+ hintinfo = xmalloc(hmeta.dls_size);
+
+ /*
+ * Next fetch both sets of paths.
+ */
+ sargs.request = RTLD_DI_SERINFO;
+ sargs.serinfo = SLPinfo;
+ sargs.serpath = &SLPinfo->dls_serpath[0];
+ sargs.strspace = (char *)&SLPinfo->dls_serpath[smeta.dls_cnt];
+
+ hargs.request = RTLD_DI_SERINFO;
+ hargs.serinfo = hintinfo;
+ hargs.serpath = &hintinfo->dls_serpath[0];
+ hargs.strspace = (char *)&hintinfo->dls_serpath[hmeta.dls_cnt];
+
+ path_enumerate(ld_standard_library_path, fill_search_info, NULL,
+ &sargs);
+ path_enumerate(hints, fill_search_info, NULL, &hargs);
+
+ /*
+ * Now calculate the difference between two sets, by excluding
+ * standard paths from the full set.
+ */
+ fndx = 0;
+ fcount = 0;
+ filtered_path = xmalloc(dirlistlen + 1);
+ hintpath = &hintinfo->dls_serpath[0];
+ for (hintndx = 0; hintndx < hmeta.dls_cnt; hintndx++, hintpath++) {
+ skip = false;
+ SLPpath = &SLPinfo->dls_serpath[0];
+ /*
+ * Check each standard path against current.
+ */
+ for (SLPndx = 0; SLPndx < smeta.dls_cnt; SLPndx++, SLPpath++) {
+ /* matched, skip the path */
+ if (!strcmp(hintpath->dls_name, SLPpath->dls_name)) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip)
+ continue;
+ /*
+ * Not matched against any standard path, add the path
+ * to result. Separate consequtive paths with ':'.
+ */
+ if (fcount > 0) {
+ filtered_path[fndx] = ':';
+ fndx++;
+ }
+ fcount++;
+ flen = strlen(hintpath->dls_name);
+ strncpy((filtered_path + fndx), hintpath->dls_name, flen);
+ fndx += flen;
+ }
+ filtered_path[fndx] = '\0';
+
+ free(SLPinfo);
+ free(hintinfo);
+
+filt_ret:
+ return (filtered_path[0] != '\0' ? filtered_path : NULL);
+}
+
+static void
+init_dag(Obj_Entry *root)
+{
+ const Needed_Entry *needed;
+ const Objlist_Entry *elm;
+ DoneList donelist;
+
+ if (root->dag_inited)
+ return;
+ donelist_init(&donelist);
+
+ /* Root object belongs to own DAG. */
+ objlist_push_tail(&root->dldags, root);
+ objlist_push_tail(&root->dagmembers, root);
+ donelist_check(&donelist, root);
+
+ /*
+ * Add dependencies of root object to DAG in breadth order
+ * by exploiting the fact that each new object get added
+ * to the tail of the dagmembers list.
+ */
+ STAILQ_FOREACH(elm, &root->dagmembers, link) {
+ for (needed = elm->obj->needed; needed != NULL;
+ needed = needed->next) {
+ if (needed->obj == NULL ||
+ donelist_check(&donelist, needed->obj))
+ continue;
+ objlist_push_tail(&needed->obj->dldags, root);
+ objlist_push_tail(&root->dagmembers, needed->obj);
+ }
+ }
+ root->dag_inited = true;
+}
+
+static void
+init_marker(Obj_Entry *marker)
+{
+ bzero(marker, sizeof(*marker));
+ marker->marker = true;
+}
+
+Obj_Entry *
+globallist_curr(const Obj_Entry *obj)
+{
+ for (;;) {
+ if (obj == NULL)
+ return (NULL);
+ if (!obj->marker)
+ return (__DECONST(Obj_Entry *, obj));
+ obj = TAILQ_PREV(obj, obj_entry_q, next);
+ }
+}
+
+Obj_Entry *
+globallist_next(const Obj_Entry *obj)
+{
+ for (;;) {
+ obj = TAILQ_NEXT(obj, next);
+ if (obj == NULL)
+ return (NULL);
+ if (!obj->marker)
+ return (__DECONST(Obj_Entry *, obj));
+ }
+}
+
+/* Prevent the object from being unmapped while the bind lock is dropped. */
+static void
+hold_object(Obj_Entry *obj)
+{
+ obj->holdcount++;
+}
+
+static void
+unhold_object(Obj_Entry *obj)
+{
+ assert(obj->holdcount > 0);
+ if (--obj->holdcount == 0 && obj->unholdfree)
+ release_object(obj);
+}
+
+static void
+process_z(Obj_Entry *root)
+{
+ const Objlist_Entry *elm;
+ Obj_Entry *obj;
+
+ /*
+ * Walk over object DAG and process every dependent object
+ * that is marked as DF_1_NODELETE or DF_1_GLOBAL. They need
+ * to grow their own DAG.
+ *
+ * For DF_1_GLOBAL, DAG is required for symbol lookups in
+ * symlook_global() to work.
+ *
+ * For DF_1_NODELETE, the DAG should have its reference upped.
+ */
+ STAILQ_FOREACH(elm, &root->dagmembers, link) {
+ obj = elm->obj;
+ if (obj == NULL)
+ continue;
+ if (obj->z_nodelete && !obj->ref_nodel) {
+ dbg("obj %s -z nodelete", obj->path);
+ init_dag(obj);
+ ref_dag(obj);
+ obj->ref_nodel = true;
+ }
+ if (obj->z_global && objlist_find(&list_global, obj) == NULL) {
+ dbg("obj %s -z global", obj->path);
+ objlist_push_tail(&list_global, obj);
+ init_dag(obj);
+ }
+ }
+}
+
+static void
+parse_rtld_phdr(Obj_Entry *obj)
+{
+ const Elf_Phdr *ph;
+ Elf_Addr note_start, note_end;
+ bool first_seg;
+
+ first_seg = true;
+ obj->stack_flags = PF_X | PF_R | PF_W;
+ for (ph = obj->phdr;
+ (const char *)ph < (const char *)obj->phdr + obj->phsize; ph++) {
+ switch (ph->p_type) {
+ case PT_LOAD:
+ if (first_seg) {
+ obj->vaddrbase = rtld_trunc_page(ph->p_vaddr);
+ first_seg = false;
+ }
+ obj->mapsize = rtld_round_page(ph->p_vaddr +
+ ph->p_memsz) - obj->vaddrbase;
+ break;
+ case PT_GNU_STACK:
+ obj->stack_flags = ph->p_flags;
+ break;
+ case PT_NOTE:
+ note_start = (Elf_Addr)obj->relocbase + ph->p_vaddr;
+ note_end = note_start + ph->p_filesz;
+ digest_notes(obj, note_start, note_end);
+ break;
+ }
+ }
+}
+
+/*
+ * Initialize the dynamic linker. The argument is the address at which
+ * the dynamic linker has been mapped into memory. The primary task of
+ * this function is to relocate the dynamic linker.
+ */
+static void
+init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info)
+{
+ Obj_Entry objtmp; /* Temporary rtld object */
+ const Elf_Ehdr *ehdr;
+ const Elf_Dyn *dyn_rpath;
+ const Elf_Dyn *dyn_soname;
+ const Elf_Dyn *dyn_runpath;
+
+ /*
+ * Conjure up an Obj_Entry structure for the dynamic linker.
+ *
+ * The "path" member can't be initialized yet because string constants
+ * cannot yet be accessed. Below we will set it correctly.
+ */
+ memset(&objtmp, 0, sizeof(objtmp));
+ objtmp.path = NULL;
+ objtmp.rtld = true;
+ objtmp.mapbase = mapbase;
+#ifdef PIC
+ objtmp.relocbase = mapbase;
+#endif
+
+ objtmp.dynamic = rtld_dynamic(&objtmp);
+ digest_dynamic1(&objtmp, 1, &dyn_rpath, &dyn_soname, &dyn_runpath);
+ assert(objtmp.needed == NULL);
+ assert(!objtmp.textrel);
+ /*
+ * Temporarily put the dynamic linker entry into the object list, so
+ * that symbols can be found.
+ */
+ relocate_objects(&objtmp, true, &objtmp, 0, NULL);
+
+ ehdr = (Elf_Ehdr *)mapbase;
+ objtmp.phdr = (Elf_Phdr *)((char *)mapbase + ehdr->e_phoff);
+ objtmp.phsize = ehdr->e_phnum * sizeof(objtmp.phdr[0]);
+
+ /* Initialize the object list. */
+ TAILQ_INIT(&obj_list);
+
+ /* Now that non-local variables can be accesses, copy out obj_rtld. */
+ memcpy(&obj_rtld, &objtmp, sizeof(obj_rtld));
+
+ /* The page size is required by the dynamic memory allocator. */
+ init_pagesizes(aux_info);
+
+ if (aux_info[AT_OSRELDATE] != NULL)
+ osreldate = aux_info[AT_OSRELDATE]->a_un.a_val;
+
+ digest_dynamic2(&obj_rtld, dyn_rpath, dyn_soname, dyn_runpath);
+
+ /* Replace the path with a dynamically allocated copy. */
+ obj_rtld.path = xstrdup(ld_path_rtld);
+
+ parse_rtld_phdr(&obj_rtld);
+ if (obj_enforce_relro(&obj_rtld) == -1)
+ rtld_die();
+
+ r_debug.r_version = R_DEBUG_VERSION;
+ r_debug.r_brk = r_debug_state;
+ r_debug.r_state = RT_CONSISTENT;
+ r_debug.r_ldbase = obj_rtld.relocbase;
+}
+
+/*
+ * Retrieve the array of supported page sizes. The kernel provides the page
+ * sizes in increasing order.
+ */
+static void
+init_pagesizes(Elf_Auxinfo **aux_info)
+{
+ static size_t psa[MAXPAGESIZES];
+ int mib[2];
+ size_t len, size;
+
+ if (aux_info[AT_PAGESIZES] != NULL &&
+ aux_info[AT_PAGESIZESLEN] != NULL) {
+ size = aux_info[AT_PAGESIZESLEN]->a_un.a_val;
+ pagesizes = aux_info[AT_PAGESIZES]->a_un.a_ptr;
+ } else {
+ len = 2;
+ if (sysctlnametomib("hw.pagesizes", mib, &len) == 0)
+ size = sizeof(psa);
+ else {
+ /* As a fallback, retrieve the base page size. */
+ size = sizeof(psa[0]);
+ if (aux_info[AT_PAGESZ] != NULL) {
+ psa[0] = aux_info[AT_PAGESZ]->a_un.a_val;
+ goto psa_filled;
+ } else {
+ mib[0] = CTL_HW;
+ mib[1] = HW_PAGESIZE;
+ len = 2;
+ }
+ }
+ if (sysctl(mib, len, psa, &size, NULL, 0) == -1) {
+ _rtld_error("sysctl for hw.pagesize(s) failed");
+ rtld_die();
+ }
+ psa_filled:
+ pagesizes = psa;
+ }
+ npagesizes = size / sizeof(pagesizes[0]);
+ /* Discard any invalid entries at the end of the array. */
+ while (npagesizes > 0 && pagesizes[npagesizes - 1] == 0)
+ npagesizes--;
+
+ page_size = pagesizes[0];
+}
+
+/*
+ * Add the init functions from a needed object list (and its recursive
+ * needed objects) to "list". This is not used directly; it is a helper
+ * function for initlist_add_objects(). The write lock must be held
+ * when this function is called.
+ */
+static void
+initlist_add_neededs(Needed_Entry *needed, Objlist *list, Objlist *iflist)
+{
+ /* Recursively process the successor needed objects. */
+ if (needed->next != NULL)
+ initlist_add_neededs(needed->next, list, iflist);
+
+ /* Process the current needed object. */
+ if (needed->obj != NULL)
+ initlist_add_objects(needed->obj, needed->obj, list, iflist);
+}
+
+/*
+ * Scan all of the DAGs rooted in the range of objects from "obj" to
+ * "tail" and add their init functions to "list". This recurses over
+ * the DAGs and ensure the proper init ordering such that each object's
+ * needed libraries are initialized before the object itself. At the
+ * same time, this function adds the objects to the global finalization
+ * list "list_fini" in the opposite order. The write lock must be
+ * held when this function is called.
+ */
+static void
+initlist_for_loaded_obj(Obj_Entry *obj, Obj_Entry *tail, Objlist *list)
+{
+ Objlist iflist; /* initfirst objs and their needed */
+ Objlist_Entry *tmp;
+
+ objlist_init(&iflist);
+ initlist_add_objects(obj, tail, list, &iflist);
+
+ STAILQ_FOREACH(tmp, &iflist, link) {
+ Obj_Entry *tobj = tmp->obj;
+
+ if ((tobj->fini != (Elf_Addr)NULL ||
+ tobj->fini_array != (Elf_Addr)NULL) &&
+ !tobj->on_fini_list) {
+ objlist_push_tail(&list_fini, tobj);
+ tobj->on_fini_list = true;
+ }
+ }
+
+ /*
+ * This might result in the same object appearing more
+ * than once on the init list. objlist_call_init()
+ * uses obj->init_scanned to avoid dup calls.
+ */
+ STAILQ_REVERSE(&iflist, Struct_Objlist_Entry, link);
+ STAILQ_FOREACH(tmp, &iflist, link)
+ objlist_push_head(list, tmp->obj);
+
+ objlist_clear(&iflist);
+}
+
+static void
+initlist_add_objects(Obj_Entry *obj, Obj_Entry *tail, Objlist *list,
+ Objlist *iflist)
+{
+ Obj_Entry *nobj;
+
+ if (obj->init_done)
+ return;
+
+ if (obj->z_initfirst || list == NULL) {
+ /*
+ * Ignore obj->init_scanned. The object might indeed
+ * already be on the init list, but due to being
+ * needed by an initfirst object, we must put it at
+ * the head of the init list. obj->init_done protects
+ * against double-initialization.
+ */
+ if (obj->needed != NULL)
+ initlist_add_neededs(obj->needed, NULL, iflist);
+ if (obj->needed_filtees != NULL)
+ initlist_add_neededs(obj->needed_filtees, NULL,
+ iflist);
+ if (obj->needed_aux_filtees != NULL)
+ initlist_add_neededs(obj->needed_aux_filtees,
+ NULL, iflist);
+ objlist_push_tail(iflist, obj);
+ } else {
+ if (obj->init_scanned)
+ return;
+ obj->init_scanned = true;
+
+ /* Recursively process the successor objects. */
+ nobj = globallist_next(obj);
+ if (nobj != NULL && obj != tail)
+ initlist_add_objects(nobj, tail, list, iflist);
+
+ /* Recursively process the needed objects. */
+ if (obj->needed != NULL)
+ initlist_add_neededs(obj->needed, list, iflist);
+ if (obj->needed_filtees != NULL)
+ initlist_add_neededs(obj->needed_filtees, list,
+ iflist);
+ if (obj->needed_aux_filtees != NULL)
+ initlist_add_neededs(obj->needed_aux_filtees, list,
+ iflist);
+
+ /* Add the object to the init list. */
+ objlist_push_tail(list, obj);
+
+ /*
+ * Add the object to the global fini list in the
+ * reverse order.
+ */
+ if ((obj->fini != (Elf_Addr)NULL ||
+ obj->fini_array != (Elf_Addr)NULL) &&
+ !obj->on_fini_list) {
+ objlist_push_head(&list_fini, obj);
+ obj->on_fini_list = true;
+ }
+ }
+}
+
+static void
+free_needed_filtees(Needed_Entry *n, RtldLockState *lockstate)
+{
+ Needed_Entry *needed, *needed1;
+
+ for (needed = n; needed != NULL; needed = needed->next) {
+ if (needed->obj != NULL) {
+ dlclose_locked(needed->obj, lockstate);
+ needed->obj = NULL;
+ }
+ }
+ for (needed = n; needed != NULL; needed = needed1) {
+ needed1 = needed->next;
+ free(needed);
+ }
+}
+
+static void
+unload_filtees(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ free_needed_filtees(obj->needed_filtees, lockstate);
+ obj->needed_filtees = NULL;
+ free_needed_filtees(obj->needed_aux_filtees, lockstate);
+ obj->needed_aux_filtees = NULL;
+ obj->filtees_loaded = false;
+}
+
+static void
+load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags,
+ RtldLockState *lockstate)
+{
+ for (; needed != NULL; needed = needed->next) {
+ needed->obj = dlopen_object(obj->strtab + needed->name, -1, obj,
+ flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW :
+ RTLD_LAZY) | RTLD_LOCAL, lockstate);
+ }
+}
+
+static void
+load_filtees(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ if (obj->filtees_loaded || obj->filtees_loading)
+ return;
+ lock_restart_for_upgrade(lockstate);
+ obj->filtees_loading = true;
+ load_filtee1(obj, obj->needed_filtees, flags, lockstate);
+ load_filtee1(obj, obj->needed_aux_filtees, flags, lockstate);
+ obj->filtees_loaded = true;
+ obj->filtees_loading = false;
+}
+
+static int
+process_needed(Obj_Entry *obj, Needed_Entry *needed, int flags)
+{
+ Obj_Entry *obj1;
+
+ for (; needed != NULL; needed = needed->next) {
+ obj1 = needed->obj = load_object(obj->strtab + needed->name, -1,
+ obj, flags & ~RTLD_LO_NOLOAD);
+ if (obj1 == NULL && !ld_tracing &&
+ (flags & RTLD_LO_FILTEES) == 0)
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Given a shared object, traverse its list of needed objects, and load
+ * each of them. Returns 0 on success. Generates an error message and
+ * returns -1 on failure.
+ */
+static int
+load_needed_objects(Obj_Entry *first, int flags)
+{
+ Obj_Entry *obj;
+
+ for (obj = first; obj != NULL; obj = TAILQ_NEXT(obj, next)) {
+ if (obj->marker)
+ continue;
+ if (process_needed(obj, obj->needed, flags) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+load_preload_objects(const char *penv, bool isfd)
+{
+ Obj_Entry *obj;
+ const char *name;
+ size_t len;
+ char savech, *p, *psave;
+ int fd;
+ static const char delim[] = " \t:;";
+
+ if (penv == NULL)
+ return (0);
+
+ p = psave = xstrdup(penv);
+ p += strspn(p, delim);
+ while (*p != '\0') {
+ len = strcspn(p, delim);
+
+ savech = p[len];
+ p[len] = '\0';
+ if (isfd) {
+ name = NULL;
+ fd = parse_integer(p);
+ if (fd == -1) {
+ free(psave);
+ return (-1);
+ }
+ } else {
+ name = p;
+ fd = -1;
+ }
+
+ obj = load_object(name, fd, NULL, 0);
+ if (obj == NULL) {
+ free(psave);
+ return (-1); /* XXX - cleanup */
+ }
+ obj->z_interpose = true;
+ p[len] = savech;
+ p += len;
+ p += strspn(p, delim);
+ }
+ LD_UTRACE(UTRACE_PRELOAD_FINISHED, NULL, NULL, 0, 0, NULL);
+
+ free(psave);
+ return (0);
+}
+
+static const char *
+printable_path(const char *path)
+{
+ return (path == NULL ? "<unknown>" : path);
+}
+
+/*
+ * Load a shared object into memory, if it is not already loaded. The
+ * object may be specified by name or by user-supplied file descriptor
+ * fd_u. In the later case, the fd_u descriptor is not closed, but its
+ * duplicate is.
+ *
+ * Returns a pointer to the Obj_Entry for the object. Returns NULL
+ * on failure.
+ */
+static Obj_Entry *
+load_object(const char *name, int fd_u, const Obj_Entry *refobj, int flags)
+{
+ Obj_Entry *obj;
+ int fd;
+ struct stat sb;
+ char *path;
+
+ fd = -1;
+ if (name != NULL) {
+ TAILQ_FOREACH(obj, &obj_list, next) {
+ if (obj->marker || obj->doomed)
+ continue;
+ if (object_match_name(obj, name))
+ return (obj);
+ }
+
+ path = find_library(name, refobj, &fd);
+ if (path == NULL)
+ return (NULL);
+ } else
+ path = NULL;
+
+ if (fd >= 0) {
+ /*
+ * search_library_pathfds() opens a fresh file descriptor for
+ * the library, so there is no need to dup().
+ */
+ } else if (fd_u == -1) {
+ /*
+ * If we didn't find a match by pathname, or the name is not
+ * supplied, open the file and check again by device and inode.
+ * This avoids false mismatches caused by multiple links or ".."
+ * in pathnames.
+ *
+ * To avoid a race, we open the file and use fstat() rather than
+ * using stat().
+ */
+ if ((fd = open(path, O_RDONLY | O_CLOEXEC | O_VERIFY)) == -1) {
+ _rtld_error("Cannot open \"%s\"", path);
+ free(path);
+ return (NULL);
+ }
+ } else {
+ fd = fcntl(fd_u, F_DUPFD_CLOEXEC, 0);
+ if (fd == -1) {
+ _rtld_error("Cannot dup fd");
+ free(path);
+ return (NULL);
+ }
+ }
+ if (fstat(fd, &sb) == -1) {
+ _rtld_error("Cannot fstat \"%s\"", printable_path(path));
+ close(fd);
+ free(path);
+ return (NULL);
+ }
+ TAILQ_FOREACH(obj, &obj_list, next) {
+ if (obj->marker || obj->doomed)
+ continue;
+ if (obj->ino == sb.st_ino && obj->dev == sb.st_dev)
+ break;
+ }
+ if (obj != NULL) {
+ if (name != NULL)
+ object_add_name(obj, name);
+ free(path);
+ close(fd);
+ return (obj);
+ }
+ if (flags & RTLD_LO_NOLOAD) {
+ free(path);
+ close(fd);
+ return (NULL);
+ }
+
+ /* First use of this object, so we must map it in */
+ obj = do_load_object(fd, name, path, &sb, flags);
+ if (obj == NULL)
+ free(path);
+ close(fd);
+
+ return (obj);
+}
+
+static Obj_Entry *
+do_load_object(int fd, const char *name, char *path, struct stat *sbp,
+ int flags)
+{
+ Obj_Entry *obj;
+ struct statfs fs;
+
+ /*
+ * First, make sure that environment variables haven't been
+ * used to circumvent the noexec flag on a filesystem.
+ * We ignore fstatfs(2) failures, since fd might reference
+ * not a file, e.g. shmfd.
+ */
+ if (dangerous_ld_env && fstatfs(fd, &fs) == 0 &&
+ (fs.f_flags & MNT_NOEXEC) != 0) {
+ _rtld_error("Cannot execute objects on %s", fs.f_mntonname);
+ return (NULL);
+ }
+
+ dbg("loading \"%s\"", printable_path(path));
+ obj = map_object(fd, printable_path(path), sbp, false);
+ if (obj == NULL)
+ return (NULL);
+
+ /*
+ * If DT_SONAME is present in the object, digest_dynamic2 already
+ * added it to the object names.
+ */
+ if (name != NULL)
+ object_add_name(obj, name);
+ obj->path = path;
+ if (!digest_dynamic(obj, 0))
+ goto errp;
+ dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d", obj->path,
+ obj->valid_hash_sysv, obj->valid_hash_gnu, obj->dynsymcount);
+ if (obj->z_pie && (flags & RTLD_LO_TRACE) == 0) {
+ dbg("refusing to load PIE executable \"%s\"", obj->path);
+ _rtld_error("Cannot load PIE binary %s as DSO", obj->path);
+ goto errp;
+ }
+ if (obj->z_noopen &&
+ (flags & (RTLD_LO_DLOPEN | RTLD_LO_TRACE)) == RTLD_LO_DLOPEN) {
+ dbg("refusing to load non-loadable \"%s\"", obj->path);
+ _rtld_error("Cannot dlopen non-loadable %s", obj->path);
+ goto errp;
+ }
+
+ obj->dlopened = (flags & RTLD_LO_DLOPEN) != 0;
+ TAILQ_INSERT_TAIL(&obj_list, obj, next);
+ obj_count++;
+ obj_loads++;
+ linkmap_add(obj); /* for GDB & dlinfo() */
+ max_stack_flags |= obj->stack_flags;
+
+ dbg(" %p .. %p: %s", obj->mapbase, obj->mapbase + obj->mapsize - 1,
+ obj->path);
+ if (obj->textrel)
+ dbg(" WARNING: %s has impure text", obj->path);
+ LD_UTRACE(UTRACE_LOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0,
+ obj->path);
+
+ return (obj);
+
+errp:
+ munmap(obj->mapbase, obj->mapsize);
+ obj_free(obj);
+ return (NULL);
+}
+
+static int
+load_kpreload(const void *addr)
+{
+ Obj_Entry *obj;
+ const Elf_Ehdr *ehdr;
+ const Elf_Phdr *phdr, *phlimit, *phdyn, *seg0, *segn;
+ static const char kname[] = "[vdso]";
+
+ ehdr = addr;
+ if (!check_elf_headers(ehdr, "kpreload"))
+ return (-1);
+ obj = obj_new();
+ phdr = (const Elf_Phdr *)((const char *)addr + ehdr->e_phoff);
+ obj->phdr = phdr;
+ obj->phsize = ehdr->e_phnum * sizeof(*phdr);
+ phlimit = phdr + ehdr->e_phnum;
+ seg0 = segn = NULL;
+
+ for (; phdr < phlimit; phdr++) {
+ switch (phdr->p_type) {
+ case PT_DYNAMIC:
+ phdyn = phdr;
+ break;
+ case PT_GNU_STACK:
+ /* Absense of PT_GNU_STACK implies stack_flags == 0. */
+ obj->stack_flags = phdr->p_flags;
+ break;
+ case PT_LOAD:
+ if (seg0 == NULL || seg0->p_vaddr > phdr->p_vaddr)
+ seg0 = phdr;
+ if (segn == NULL ||
+ segn->p_vaddr + segn->p_memsz <
+ phdr->p_vaddr + phdr->p_memsz)
+ segn = phdr;
+ break;
+ }
+ }
+
+ obj->mapbase = __DECONST(caddr_t, addr);
+ obj->mapsize = segn->p_vaddr + segn->p_memsz;
+ obj->vaddrbase = 0;
+ obj->relocbase = obj->mapbase;
+
+ object_add_name(obj, kname);
+ obj->path = xstrdup(kname);
+ obj->dynamic = (const Elf_Dyn *)(obj->relocbase + phdyn->p_vaddr);
+
+ if (!digest_dynamic(obj, 0)) {
+ obj_free(obj);
+ return (-1);
+ }
+
+ /*
+ * We assume that kernel-preloaded object does not need
+ * relocation. It is currently written into read-only page,
+ * handling relocations would mean we need to allocate at
+ * least one additional page per AS.
+ */
+ dbg("%s mapbase %p phdrs %p PT_LOAD phdr %p vaddr %p dynamic %p",
+ obj->path, obj->mapbase, obj->phdr, seg0,
+ obj->relocbase + seg0->p_vaddr, obj->dynamic);
+
+ TAILQ_INSERT_TAIL(&obj_list, obj, next);
+ obj_count++;
+ obj_loads++;
+ linkmap_add(obj); /* for GDB & dlinfo() */
+ max_stack_flags |= obj->stack_flags;
+
+ LD_UTRACE(UTRACE_LOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0,
+ obj->path);
+ return (0);
+}
+
+Obj_Entry *
+obj_from_addr(const void *addr)
+{
+ Obj_Entry *obj;
+
+ TAILQ_FOREACH(obj, &obj_list, next) {
+ if (obj->marker)
+ continue;
+ if (addr < (void *)obj->mapbase)
+ continue;
+ if (addr < (void *)(obj->mapbase + obj->mapsize))
+ return obj;
+ }
+ return (NULL);
+}
+
+static void
+preinit_main(void)
+{
+ Elf_Addr *preinit_addr;
+ int index;
+
+ preinit_addr = (Elf_Addr *)obj_main->preinit_array;
+ if (preinit_addr == NULL)
+ return;
+
+ for (index = 0; index < obj_main->preinit_array_num; index++) {
+ if (preinit_addr[index] != 0 && preinit_addr[index] != 1) {
+ dbg("calling preinit function for %s at %p",
+ obj_main->path, (void *)preinit_addr[index]);
+ LD_UTRACE(UTRACE_INIT_CALL, obj_main,
+ (void *)preinit_addr[index], 0, 0, obj_main->path);
+ call_init_pointer(obj_main, preinit_addr[index]);
+ }
+ }
+}
+
+/*
+ * Call the finalization functions for each of the objects in "list"
+ * belonging to the DAG of "root" and referenced once. If NULL "root"
+ * is specified, every finalization function will be called regardless
+ * of the reference count and the list elements won't be freed. All of
+ * the objects are expected to have non-NULL fini functions.
+ */
+static void
+objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate)
+{
+ Objlist_Entry *elm;
+ struct dlerror_save *saved_msg;
+ Elf_Addr *fini_addr;
+ int index;
+
+ assert(root == NULL || root->refcount == 1);
+
+ if (root != NULL)
+ root->doomed = true;
+
+ /*
+ * Preserve the current error message since a fini function might
+ * call into the dynamic linker and overwrite it.
+ */
+ saved_msg = errmsg_save();
+ do {
+ STAILQ_FOREACH(elm, list, link) {
+ if (root != NULL &&
+ (elm->obj->refcount != 1 ||
+ objlist_find(&root->dagmembers, elm->obj) ==
+ NULL))
+ continue;
+ /* Remove object from fini list to prevent recursive
+ * invocation. */
+ STAILQ_REMOVE(list, elm, Struct_Objlist_Entry, link);
+ /* Ensure that new references cannot be acquired. */
+ elm->obj->doomed = true;
+
+ hold_object(elm->obj);
+ lock_release(rtld_bind_lock, lockstate);
+ /*
+ * It is legal to have both DT_FINI and DT_FINI_ARRAY
+ * defined. When this happens, DT_FINI_ARRAY is
+ * processed first.
+ */
+ fini_addr = (Elf_Addr *)elm->obj->fini_array;
+ if (fini_addr != NULL && elm->obj->fini_array_num > 0) {
+ for (index = elm->obj->fini_array_num - 1;
+ index >= 0; index--) {
+ if (fini_addr[index] != 0 &&
+ fini_addr[index] != 1) {
+ dbg("calling fini function for %s at %p",
+ elm->obj->path,
+ (void *)fini_addr[index]);
+ LD_UTRACE(UTRACE_FINI_CALL,
+ elm->obj,
+ (void *)fini_addr[index], 0,
+ 0, elm->obj->path);
+ call_initfini_pointer(elm->obj,
+ fini_addr[index]);
+ }
+ }
+ }
+ if (elm->obj->fini != (Elf_Addr)NULL) {
+ dbg("calling fini function for %s at %p",
+ elm->obj->path, (void *)elm->obj->fini);
+ LD_UTRACE(UTRACE_FINI_CALL, elm->obj,
+ (void *)elm->obj->fini, 0, 0,
+ elm->obj->path);
+ call_initfini_pointer(elm->obj, elm->obj->fini);
+ }
+ wlock_acquire(rtld_bind_lock, lockstate);
+ unhold_object(elm->obj);
+ /* No need to free anything if process is going down. */
+ if (root != NULL)
+ free(elm);
+ /*
+ * We must restart the list traversal after every fini
+ * call because a dlclose() call from the fini function
+ * or from another thread might have modified the
+ * reference counts.
+ */
+ break;
+ }
+ } while (elm != NULL);
+ errmsg_restore(saved_msg);
+}
+
+/*
+ * Call the initialization functions for each of the objects in
+ * "list". All of the objects are expected to have non-NULL init
+ * functions.
+ */
+static void
+objlist_call_init(Objlist *list, RtldLockState *lockstate)
+{
+ Objlist_Entry *elm;
+ Obj_Entry *obj;
+ struct dlerror_save *saved_msg;
+ Elf_Addr *init_addr;
+ void (*reg)(void (*)(void));
+ int index;
+
+ /*
+ * Clean init_scanned flag so that objects can be rechecked and
+ * possibly initialized earlier if any of vectors called below
+ * cause the change by using dlopen.
+ */
+ TAILQ_FOREACH(obj, &obj_list, next) {
+ if (obj->marker)
+ continue;
+ obj->init_scanned = false;
+ }
+
+ /*
+ * Preserve the current error message since an init function might
+ * call into the dynamic linker and overwrite it.
+ */
+ saved_msg = errmsg_save();
+ STAILQ_FOREACH(elm, list, link) {
+ if (elm->obj->init_done) /* Initialized early. */
+ continue;
+ /*
+ * Race: other thread might try to use this object before
+ * current one completes the initialization. Not much can be
+ * done here without better locking.
+ */
+ elm->obj->init_done = true;
+ hold_object(elm->obj);
+ reg = NULL;
+ if (elm->obj == obj_main && obj_main->crt_no_init) {
+ reg = (void (*)(void (*)(void)))
+ get_program_var_addr("__libc_atexit", lockstate);
+ }
+ lock_release(rtld_bind_lock, lockstate);
+ if (reg != NULL) {
+ reg(rtld_exit);
+ rtld_exit_ptr = rtld_nop_exit;
+ }
+
+ /*
+ * It is legal to have both DT_INIT and DT_INIT_ARRAY defined.
+ * When this happens, DT_INIT is processed first.
+ */
+ if (elm->obj->init != (Elf_Addr)NULL) {
+ dbg("calling init function for %s at %p",
+ elm->obj->path, (void *)elm->obj->init);
+ LD_UTRACE(UTRACE_INIT_CALL, elm->obj,
+ (void *)elm->obj->init, 0, 0, elm->obj->path);
+ call_init_pointer(elm->obj, elm->obj->init);
+ }
+ init_addr = (Elf_Addr *)elm->obj->init_array;
+ if (init_addr != NULL) {
+ for (index = 0; index < elm->obj->init_array_num;
+ index++) {
+ if (init_addr[index] != 0 &&
+ init_addr[index] != 1) {
+ dbg("calling init function for %s at %p",
+ elm->obj->path,
+ (void *)init_addr[index]);
+ LD_UTRACE(UTRACE_INIT_CALL, elm->obj,
+ (void *)init_addr[index], 0, 0,
+ elm->obj->path);
+ call_init_pointer(elm->obj,
+ init_addr[index]);
+ }
+ }
+ }
+ wlock_acquire(rtld_bind_lock, lockstate);
+ unhold_object(elm->obj);
+ }
+ errmsg_restore(saved_msg);
+}
+
+static void
+objlist_clear(Objlist *list)
+{
+ Objlist_Entry *elm;
+
+ while (!STAILQ_EMPTY(list)) {
+ elm = STAILQ_FIRST(list);
+ STAILQ_REMOVE_HEAD(list, link);
+ free(elm);
+ }
+}
+
+static Objlist_Entry *
+objlist_find(Objlist *list, const Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ STAILQ_FOREACH(elm, list, link)
+ if (elm->obj == obj)
+ return elm;
+ return (NULL);
+}
+
+static void
+objlist_init(Objlist *list)
+{
+ STAILQ_INIT(list);
+}
+
+static void
+objlist_push_head(Objlist *list, Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ elm = NEW(Objlist_Entry);
+ elm->obj = obj;
+ STAILQ_INSERT_HEAD(list, elm, link);
+}
+
+static void
+objlist_push_tail(Objlist *list, Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ elm = NEW(Objlist_Entry);
+ elm->obj = obj;
+ STAILQ_INSERT_TAIL(list, elm, link);
+}
+
+static void
+objlist_put_after(Objlist *list, Obj_Entry *listobj, Obj_Entry *obj)
+{
+ Objlist_Entry *elm, *listelm;
+
+ STAILQ_FOREACH(listelm, list, link) {
+ if (listelm->obj == listobj)
+ break;
+ }
+ elm = NEW(Objlist_Entry);
+ elm->obj = obj;
+ if (listelm != NULL)
+ STAILQ_INSERT_AFTER(list, listelm, elm, link);
+ else
+ STAILQ_INSERT_TAIL(list, elm, link);
+}
+
+static void
+objlist_remove(Objlist *list, Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ if ((elm = objlist_find(list, obj)) != NULL) {
+ STAILQ_REMOVE(list, elm, Struct_Objlist_Entry, link);
+ free(elm);
+ }
+}
+
+/*
+ * Relocate dag rooted in the specified object.
+ * Returns 0 on success, or -1 on failure.
+ */
+
+static int
+relocate_object_dag(Obj_Entry *root, bool bind_now, Obj_Entry *rtldobj,
+ int flags, RtldLockState *lockstate)
+{
+ Objlist_Entry *elm;
+ int error;
+
+ error = 0;
+ STAILQ_FOREACH(elm, &root->dagmembers, link) {
+ error = relocate_object(elm->obj, bind_now, rtldobj, flags,
+ lockstate);
+ if (error == -1)
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Prepare for, or clean after, relocating an object marked with
+ * DT_TEXTREL or DF_TEXTREL. Before relocating, all read-only
+ * segments are remapped read-write. After relocations are done, the
+ * segment's permissions are returned back to the modes specified in
+ * the phdrs. If any relocation happened, or always for wired
+ * program, COW is triggered.
+ */
+static int
+reloc_textrel_prot(Obj_Entry *obj, bool before)
+{
+ const Elf_Phdr *ph;
+ void *base;
+ size_t l, sz;
+ int prot;
+
+ for (l = obj->phsize / sizeof(*ph), ph = obj->phdr; l > 0; l--, ph++) {
+ if (ph->p_type != PT_LOAD || (ph->p_flags & PF_W) != 0)
+ continue;
+ base = obj->relocbase + rtld_trunc_page(ph->p_vaddr);
+ sz = rtld_round_page(ph->p_vaddr + ph->p_filesz) -
+ rtld_trunc_page(ph->p_vaddr);
+ prot = before ? (PROT_READ | PROT_WRITE) :
+ convert_prot(ph->p_flags);
+ if (mprotect(base, sz, prot) == -1) {
+ _rtld_error("%s: Cannot write-%sable text segment: %s",
+ obj->path, before ? "en" : "dis",
+ rtld_strerror(errno));
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/* Process RELR relative relocations. */
+static void
+reloc_relr(Obj_Entry *obj)
+{
+ const Elf_Relr *relr, *relrlim;
+ Elf_Addr *where;
+
+ relrlim = (const Elf_Relr *)((const char *)obj->relr + obj->relrsize);
+ for (relr = obj->relr; relr < relrlim; relr++) {
+ Elf_Relr entry = *relr;
+
+ if ((entry & 1) == 0) {
+ where = (Elf_Addr *)(obj->relocbase + entry);
+ *where++ += (Elf_Addr)obj->relocbase;
+ } else {
+ for (long i = 0; (entry >>= 1) != 0; i++)
+ if ((entry & 1) != 0)
+ where[i] += (Elf_Addr)obj->relocbase;
+ where += CHAR_BIT * sizeof(Elf_Relr) - 1;
+ }
+ }
+}
+
+/*
+ * Relocate single object.
+ * Returns 0 on success, or -1 on failure.
+ */
+static int
+relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, int flags,
+ RtldLockState *lockstate)
+{
+ if (obj->relocated)
+ return (0);
+ obj->relocated = true;
+ if (obj != rtldobj)
+ dbg("relocating \"%s\"", obj->path);
+
+ if (obj->symtab == NULL || obj->strtab == NULL ||
+ !(obj->valid_hash_sysv || obj->valid_hash_gnu))
+ dbg("object %s has no run-time symbol table", obj->path);
+
+ /* There are relocations to the write-protected text segment. */
+ if (obj->textrel && reloc_textrel_prot(obj, true) != 0)
+ return (-1);
+
+ /* Process the non-PLT non-IFUNC relocations. */
+ if (reloc_non_plt(obj, rtldobj, flags, lockstate))
+ return (-1);
+ reloc_relr(obj);
+
+ /* Re-protected the text segment. */
+ if (obj->textrel && reloc_textrel_prot(obj, false) != 0)
+ return (-1);
+
+ /* Set the special PLT or GOT entries. */
+ init_pltgot(obj);
+
+ /* Process the PLT relocations. */
+ if (reloc_plt(obj, flags, lockstate) == -1)
+ return (-1);
+ /* Relocate the jump slots if we are doing immediate binding. */
+ if ((obj->bind_now || bind_now) &&
+ reloc_jmpslots(obj, flags, lockstate) == -1)
+ return (-1);
+
+ if (obj != rtldobj && !obj->mainprog && obj_enforce_relro(obj) == -1)
+ return (-1);
+
+ /*
+ * Set up the magic number and version in the Obj_Entry. These
+ * were checked in the crt1.o from the original ElfKit, so we
+ * set them for backward compatibility.
+ */
+ obj->magic = RTLD_MAGIC;
+ obj->version = RTLD_VERSION;
+
+ return (0);
+}
+
+/*
+ * Relocate newly-loaded shared objects. The argument is a pointer to
+ * the Obj_Entry for the first such object. All objects from the first
+ * to the end of the list of objects are relocated. Returns 0 on success,
+ * or -1 on failure.
+ */
+static int
+relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj, int flags,
+ RtldLockState *lockstate)
+{
+ Obj_Entry *obj;
+ int error;
+
+ for (error = 0, obj = first; obj != NULL; obj = TAILQ_NEXT(obj, next)) {
+ if (obj->marker)
+ continue;
+ error = relocate_object(obj, bind_now, rtldobj, flags,
+ lockstate);
+ if (error == -1)
+ break;
+ }
+ return (error);
+}
+
+/*
+ * The handling of R_MACHINE_IRELATIVE relocations and jumpslots
+ * referencing STT_GNU_IFUNC symbols is postponed till the other
+ * relocations are done. The indirect functions specified as
+ * ifunc are allowed to call other symbols, so we need to have
+ * objects relocated before asking for resolution from indirects.
+ *
+ * The R_MACHINE_IRELATIVE slots are resolved in greedy fashion,
+ * instead of the usual lazy handling of PLT slots. It is
+ * consistent with how GNU does it.
+ */
+static int
+resolve_object_ifunc(Obj_Entry *obj, bool bind_now, int flags,
+ RtldLockState *lockstate)
+{
+ if (obj->ifuncs_resolved)
+ return (0);
+ obj->ifuncs_resolved = true;
+ if (!obj->irelative && !obj->irelative_nonplt &&
+ !((obj->bind_now || bind_now) && obj->gnu_ifunc) &&
+ !obj->non_plt_gnu_ifunc)
+ return (0);
+ if (obj_disable_relro(obj) == -1 ||
+ (obj->irelative && reloc_iresolve(obj, lockstate) == -1) ||
+ (obj->irelative_nonplt &&
+ reloc_iresolve_nonplt(obj, lockstate) == -1) ||
+ ((obj->bind_now || bind_now) && obj->gnu_ifunc &&
+ reloc_gnu_ifunc(obj, flags, lockstate) == -1) ||
+ (obj->non_plt_gnu_ifunc &&
+ reloc_non_plt(obj, &obj_rtld, flags | SYMLOOK_IFUNC,
+ lockstate) == -1) ||
+ obj_enforce_relro(obj) == -1)
+ return (-1);
+ return (0);
+}
+
+static int
+initlist_objects_ifunc(Objlist *list, bool bind_now, int flags,
+ RtldLockState *lockstate)
+{
+ Objlist_Entry *elm;
+ Obj_Entry *obj;
+
+ STAILQ_FOREACH(elm, list, link) {
+ obj = elm->obj;
+ if (obj->marker)
+ continue;
+ if (resolve_object_ifunc(obj, bind_now, flags, lockstate) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Cleanup procedure. It will be called (by the atexit mechanism) just
+ * before the process exits.
+ */
+static void
+rtld_exit(void)
+{
+ RtldLockState lockstate;
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ dbg("rtld_exit()");
+ objlist_call_fini(&list_fini, NULL, &lockstate);
+ /* No need to remove the items from the list, since we are exiting. */
+ if (!libmap_disable)
+ lm_fini();
+ lock_release(rtld_bind_lock, &lockstate);
+}
+
+static void
+rtld_nop_exit(void)
+{
+}
+
+/*
+ * Iterate over a search path, translate each element, and invoke the
+ * callback on the result.
+ */
+static void *
+path_enumerate(const char *path, path_enum_proc callback,
+ const char *refobj_path, void *arg)
+{
+ const char *trans;
+ if (path == NULL)
+ return (NULL);
+
+ path += strspn(path, ":;");
+ while (*path != '\0') {
+ size_t len;
+ char *res;
+
+ len = strcspn(path, ":;");
+ trans = lm_findn(refobj_path, path, len);
+ if (trans)
+ res = callback(trans, strlen(trans), arg);
+ else
+ res = callback(path, len, arg);
+
+ if (res != NULL)
+ return (res);
+
+ path += len;
+ path += strspn(path, ":;");
+ }
+
+ return (NULL);
+}
+
+struct try_library_args {
+ const char *name;
+ size_t namelen;
+ char *buffer;
+ size_t buflen;
+ int fd;
+};
+
+static void *
+try_library_path(const char *dir, size_t dirlen, void *param)
+{
+ struct try_library_args *arg;
+ int fd;
+
+ arg = param;
+ if (*dir == '/' || trust) {
+ char *pathname;
+
+ if (dirlen + 1 + arg->namelen + 1 > arg->buflen)
+ return (NULL);
+
+ pathname = arg->buffer;
+ strncpy(pathname, dir, dirlen);
+ pathname[dirlen] = '/';
+ strcpy(pathname + dirlen + 1, arg->name);
+
+ dbg(" Trying \"%s\"", pathname);
+ fd = open(pathname, O_RDONLY | O_CLOEXEC | O_VERIFY);
+ if (fd >= 0) {
+ dbg(" Opened \"%s\", fd %d", pathname, fd);
+ pathname = xmalloc(dirlen + 1 + arg->namelen + 1);
+ strcpy(pathname, arg->buffer);
+ arg->fd = fd;
+ return (pathname);
+ } else {
+ dbg(" Failed to open \"%s\": %s", pathname,
+ rtld_strerror(errno));
+ }
+ }
+ return (NULL);
+}
+
+static char *
+search_library_path(const char *name, const char *path, const char *refobj_path,
+ int *fdp)
+{
+ char *p;
+ struct try_library_args arg;
+
+ if (path == NULL)
+ return (NULL);
+
+ arg.name = name;
+ arg.namelen = strlen(name);
+ arg.buffer = xmalloc(PATH_MAX);
+ arg.buflen = PATH_MAX;
+ arg.fd = -1;
+
+ p = path_enumerate(path, try_library_path, refobj_path, &arg);
+ *fdp = arg.fd;
+
+ free(arg.buffer);
+
+ return (p);
+}
+
+/*
+ * Finds the library with the given name using the directory descriptors
+ * listed in the LD_LIBRARY_PATH_FDS environment variable.
+ *
+ * Returns a freshly-opened close-on-exec file descriptor for the library,
+ * or -1 if the library cannot be found.
+ */
+static char *
+search_library_pathfds(const char *name, const char *path, int *fdp)
+{
+ char *envcopy, *fdstr, *found, *last_token;
+ size_t len;
+ int dirfd, fd;
+
+ dbg("%s('%s', '%s', fdp)", __func__, name, path);
+
+ /* Don't load from user-specified libdirs into setuid binaries. */
+ if (!trust)
+ return (NULL);
+
+ /* We can't do anything if LD_LIBRARY_PATH_FDS isn't set. */
+ if (path == NULL)
+ return (NULL);
+
+ /* LD_LIBRARY_PATH_FDS only works with relative paths. */
+ if (name[0] == '/') {
+ dbg("Absolute path (%s) passed to %s", name, __func__);
+ return (NULL);
+ }
+
+ /*
+ * Use strtok_r() to walk the FD:FD:FD list. This requires a local
+ * copy of the path, as strtok_r rewrites separator tokens
+ * with '\0'.
+ */
+ found = NULL;
+ envcopy = xstrdup(path);
+ for (fdstr = strtok_r(envcopy, ":", &last_token); fdstr != NULL;
+ fdstr = strtok_r(NULL, ":", &last_token)) {
+ dirfd = parse_integer(fdstr);
+ if (dirfd < 0) {
+ _rtld_error("failed to parse directory FD: '%s'",
+ fdstr);
+ break;
+ }
+ fd = __sys_openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_VERIFY);
+ if (fd >= 0) {
+ *fdp = fd;
+ len = strlen(fdstr) + strlen(name) + 3;
+ found = xmalloc(len);
+ if (rtld_snprintf(found, len, "#%d/%s", dirfd, name) <
+ 0) {
+ _rtld_error("error generating '%d/%s'", dirfd,
+ name);
+ rtld_die();
+ }
+ dbg("open('%s') => %d", found, fd);
+ break;
+ }
+ }
+ free(envcopy);
+
+ return (found);
+}
+
+int
+dlclose(void *handle)
+{
+ RtldLockState lockstate;
+ int error;
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ error = dlclose_locked(handle, &lockstate);
+ lock_release(rtld_bind_lock, &lockstate);
+ return (error);
+}
+
+static int
+dlclose_locked(void *handle, RtldLockState *lockstate)
+{
+ Obj_Entry *root;
+
+ root = dlcheck(handle);
+ if (root == NULL)
+ return (-1);
+ LD_UTRACE(UTRACE_DLCLOSE_START, handle, NULL, 0, root->dl_refcount,
+ root->path);
+
+ /* Unreference the object and its dependencies. */
+ root->dl_refcount--;
+
+ if (root->refcount == 1) {
+ /*
+ * The object will be no longer referenced, so we must unload
+ * it. First, call the fini functions.
+ */
+ objlist_call_fini(&list_fini, root, lockstate);
+
+ unref_dag(root);
+
+ /* Finish cleaning up the newly-unreferenced objects. */
+ GDB_STATE(RT_DELETE, &root->linkmap);
+ unload_object(root, lockstate);
+ GDB_STATE(RT_CONSISTENT, NULL);
+ } else
+ unref_dag(root);
+
+ LD_UTRACE(UTRACE_DLCLOSE_STOP, handle, NULL, 0, 0, NULL);
+ return (0);
+}
+
+char *
+dlerror(void)
+{
+ if (*(lockinfo.dlerror_seen()) != 0)
+ return (NULL);
+ *lockinfo.dlerror_seen() = 1;
+ return (lockinfo.dlerror_loc());
+}
+
+/*
+ * This function is deprecated and has no effect.
+ */
+void
+dllockinit(void *context, void *(*_lock_create)(void *context)__unused,
+ void (*_rlock_acquire)(void *lock) __unused,
+ void (*_wlock_acquire)(void *lock) __unused,
+ void (*_lock_release)(void *lock) __unused,
+ void (*_lock_destroy)(void *lock) __unused,
+ void (*context_destroy)(void *context))
+{
+ static void *cur_context;
+ static void (*cur_context_destroy)(void *);
+
+ /* Just destroy the context from the previous call, if necessary. */
+ if (cur_context_destroy != NULL)
+ cur_context_destroy(cur_context);
+ cur_context = context;
+ cur_context_destroy = context_destroy;
+}
+
+void *
+dlopen(const char *name, int mode)
+{
+ return (rtld_dlopen(name, -1, mode));
+}
+
+void *
+fdlopen(int fd, int mode)
+{
+ return (rtld_dlopen(NULL, fd, mode));
+}
+
+static void *
+rtld_dlopen(const char *name, int fd, int mode)
+{
+ RtldLockState lockstate;
+ int lo_flags;
+
+ LD_UTRACE(UTRACE_DLOPEN_START, NULL, NULL, 0, mode, name);
+ ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1";
+ if (ld_tracing != NULL) {
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ if (sigsetjmp(lockstate.env, 0) != 0)
+ lock_upgrade(rtld_bind_lock, &lockstate);
+ environ = __DECONST(char **,
+ *get_program_var_addr("environ", &lockstate));
+ lock_release(rtld_bind_lock, &lockstate);
+ }
+ lo_flags = RTLD_LO_DLOPEN;
+ if (mode & RTLD_NODELETE)
+ lo_flags |= RTLD_LO_NODELETE;
+ if (mode & RTLD_NOLOAD)
+ lo_flags |= RTLD_LO_NOLOAD;
+ if (mode & RTLD_DEEPBIND)
+ lo_flags |= RTLD_LO_DEEPBIND;
+ if (ld_tracing != NULL)
+ lo_flags |= RTLD_LO_TRACE | RTLD_LO_IGNSTLS;
+
+ return (dlopen_object(name, fd, obj_main, lo_flags,
+ mode & (RTLD_MODEMASK | RTLD_GLOBAL), NULL));
+}
+
+static void
+dlopen_cleanup(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ obj->dl_refcount--;
+ unref_dag(obj);
+ if (obj->refcount == 0)
+ unload_object(obj, lockstate);
+}
+
+static Obj_Entry *
+dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags,
+ int mode, RtldLockState *lockstate)
+{
+ Obj_Entry *obj;
+ Objlist initlist;
+ RtldLockState mlockstate;
+ int result;
+
+ dbg(
+ "dlopen_object name \"%s\" fd %d refobj \"%s\" lo_flags %#x mode %#x",
+ name != NULL ? name : "<null>", fd,
+ refobj == NULL ? "<null>" : refobj->path, lo_flags, mode);
+ objlist_init(&initlist);
+
+ if (lockstate == NULL && !(lo_flags & RTLD_LO_EARLY)) {
+ wlock_acquire(rtld_bind_lock, &mlockstate);
+ lockstate = &mlockstate;
+ }
+ GDB_STATE(RT_ADD, NULL);
+
+ obj = NULL;
+ if (name == NULL && fd == -1) {
+ obj = obj_main;
+ obj->refcount++;
+ } else {
+ obj = load_object(name, fd, refobj, lo_flags);
+ }
+
+ if (obj != NULL) {
+ obj->dl_refcount++;
+ if ((mode & RTLD_GLOBAL) != 0 &&
+ objlist_find(&list_global, obj) == NULL)
+ objlist_push_tail(&list_global, obj);
+
+ if (!obj->init_done) {
+ /* We loaded something new and have to init something.
+ */
+ if ((lo_flags & RTLD_LO_DEEPBIND) != 0)
+ obj->deepbind = true;
+ result = 0;
+ if ((lo_flags & (RTLD_LO_EARLY |
+ RTLD_LO_IGNSTLS)) == 0 &&
+ obj->static_tls && !allocate_tls_offset(obj)) {
+ _rtld_error(
+ "%s: No space available for static Thread Local Storage",
+ obj->path);
+ result = -1;
+ }
+ if (result != -1)
+ result = load_needed_objects(obj,
+ lo_flags & (RTLD_LO_DLOPEN | RTLD_LO_EARLY |
+ RTLD_LO_IGNSTLS | RTLD_LO_TRACE));
+ init_dag(obj);
+ ref_dag(obj);
+ if (result != -1)
+ result = rtld_verify_versions(&obj->dagmembers);
+ if (result != -1 && ld_tracing)
+ goto trace;
+ if (result == -1 || relocate_object_dag(obj,
+ (mode & RTLD_MODEMASK) == RTLD_NOW, &obj_rtld,
+ (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0,
+ lockstate) == -1) {
+ dlopen_cleanup(obj, lockstate);
+ obj = NULL;
+ } else if ((lo_flags & RTLD_LO_EARLY) != 0) {
+ /*
+ * Do not call the init functions for early
+ * loaded filtees. The image is still not
+ * initialized enough for them to work.
+ *
+ * Our object is found by the global object list
+ * and will be ordered among all init calls done
+ * right before transferring control to main.
+ */
+ } else {
+ /* Make list of init functions to call. */
+ initlist_for_loaded_obj(obj, obj, &initlist);
+ }
+ /*
+ * Process all no_delete or global objects here, given
+ * them own DAGs to prevent their dependencies from
+ * being unloaded. This has to be done after we have
+ * loaded all of the dependencies, so that we do not
+ * miss any.
+ */
+ if (obj != NULL)
+ process_z(obj);
+ } else {
+ /*
+ * Bump the reference counts for objects on this DAG. If
+ * this is the first dlopen() call for the object that
+ * was already loaded as a dependency, initialize the
+ * dag starting at it.
+ */
+ init_dag(obj);
+ ref_dag(obj);
+
+ if ((lo_flags & RTLD_LO_TRACE) != 0)
+ goto trace;
+ }
+ if (obj != NULL &&
+ ((lo_flags & RTLD_LO_NODELETE) != 0 || obj->z_nodelete) &&
+ !obj->ref_nodel) {
+ dbg("obj %s nodelete", obj->path);
+ ref_dag(obj);
+ obj->z_nodelete = obj->ref_nodel = true;
+ }
+ }
+
+ LD_UTRACE(UTRACE_DLOPEN_STOP, obj, NULL, 0, obj ? obj->dl_refcount : 0,
+ name);
+ GDB_STATE(RT_CONSISTENT, obj ? &obj->linkmap : NULL);
+
+ if ((lo_flags & RTLD_LO_EARLY) == 0) {
+ map_stacks_exec(lockstate);
+ if (obj != NULL)
+ distribute_static_tls(&initlist);
+ }
+
+ if (initlist_objects_ifunc(&initlist, (mode & RTLD_MODEMASK) ==
+ RTLD_NOW, (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0,
+ lockstate) == -1) {
+ objlist_clear(&initlist);
+ dlopen_cleanup(obj, lockstate);
+ if (lockstate == &mlockstate)
+ lock_release(rtld_bind_lock, lockstate);
+ return (NULL);
+ }
+
+ if ((lo_flags & RTLD_LO_EARLY) == 0) {
+ /* Call the init functions. */
+ objlist_call_init(&initlist, lockstate);
+ }
+ objlist_clear(&initlist);
+ if (lockstate == &mlockstate)
+ lock_release(rtld_bind_lock, lockstate);
+ return (obj);
+trace:
+ trace_loaded_objects(obj, false);
+ if (lockstate == &mlockstate)
+ lock_release(rtld_bind_lock, lockstate);
+ exit(0);
+}
+
+static void *
+do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
+ int flags)
+{
+ DoneList donelist;
+ const Obj_Entry *obj, *defobj;
+ const Elf_Sym *def;
+ SymLook req;
+ RtldLockState lockstate;
+ tls_index ti;
+ void *sym;
+ int res;
+
+ def = NULL;
+ defobj = NULL;
+ symlook_init(&req, name);
+ req.ventry = ve;
+ req.flags = flags | SYMLOOK_IN_PLT;
+ req.lockstate = &lockstate;
+
+ LD_UTRACE(UTRACE_DLSYM_START, handle, NULL, 0, 0, name);
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ if (sigsetjmp(lockstate.env, 0) != 0)
+ lock_upgrade(rtld_bind_lock, &lockstate);
+ if (handle == NULL || handle == RTLD_NEXT || handle == RTLD_DEFAULT ||
+ handle == RTLD_SELF) {
+ if ((obj = obj_from_addr(retaddr)) == NULL) {
+ _rtld_error("Cannot determine caller's shared object");
+ lock_release(rtld_bind_lock, &lockstate);
+ LD_UTRACE(UTRACE_DLSYM_STOP, handle, NULL, 0, 0, name);
+ return (NULL);
+ }
+ if (handle == NULL) { /* Just the caller's shared object. */
+ res = symlook_obj(&req, obj);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ } else if (handle == RTLD_NEXT || /* Objects after caller's */
+ handle == RTLD_SELF) { /* ... caller included */
+ if (handle == RTLD_NEXT)
+ obj = globallist_next(obj);
+ for (; obj != NULL; obj = TAILQ_NEXT(obj, next)) {
+ if (obj->marker)
+ continue;
+ res = symlook_obj(&req, obj);
+ if (res == 0) {
+ if (def == NULL ||
+ (ld_dynamic_weak &&
+ ELF_ST_BIND(
+ req.sym_out->st_info) !=
+ STB_WEAK)) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ if (!ld_dynamic_weak ||
+ ELF_ST_BIND(def->st_info) !=
+ STB_WEAK)
+ break;
+ }
+ }
+ }
+ /*
+ * Search the dynamic linker itself, and possibly
+ * resolve the symbol from there. This is how the
+ * application links to dynamic linker services such as
+ * dlopen. Note that we ignore ld_dynamic_weak == false
+ * case, always overriding weak symbols by rtld
+ * definitions.
+ */
+ if (def == NULL ||
+ ELF_ST_BIND(def->st_info) == STB_WEAK) {
+ res = symlook_obj(&req, &obj_rtld);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ }
+ } else {
+ assert(handle == RTLD_DEFAULT);
+ res = symlook_default(&req, obj);
+ if (res == 0) {
+ defobj = req.defobj_out;
+ def = req.sym_out;
+ }
+ }
+ } else {
+ if ((obj = dlcheck(handle)) == NULL) {
+ lock_release(rtld_bind_lock, &lockstate);
+ LD_UTRACE(UTRACE_DLSYM_STOP, handle, NULL, 0, 0, name);
+ return (NULL);
+ }
+
+ donelist_init(&donelist);
+ if (obj->mainprog) {
+ /* Handle obtained by dlopen(NULL, ...) implies global
+ * scope. */
+ res = symlook_global(&req, &donelist);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ /*
+ * Search the dynamic linker itself, and possibly
+ * resolve the symbol from there. This is how the
+ * application links to dynamic linker services such as
+ * dlopen.
+ */
+ if (def == NULL ||
+ ELF_ST_BIND(def->st_info) == STB_WEAK) {
+ res = symlook_obj(&req, &obj_rtld);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ }
+ } else {
+ /* Search the whole DAG rooted at the given object. */
+ res = symlook_list(&req, &obj->dagmembers, &donelist);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ }
+ }
+
+ if (def != NULL) {
+ lock_release(rtld_bind_lock, &lockstate);
+
+ /*
+ * The value required by the caller is derived from the value
+ * of the symbol. this is simply the relocated value of the
+ * symbol.
+ */
+ if (ELF_ST_TYPE(def->st_info) == STT_FUNC)
+ sym = make_function_pointer(def, defobj);
+ else if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC)
+ sym = rtld_resolve_ifunc(defobj, def);
+ else if (ELF_ST_TYPE(def->st_info) == STT_TLS) {
+ ti.ti_module = defobj->tlsindex;
+ ti.ti_offset = def->st_value - TLS_DTV_OFFSET;
+ sym = __tls_get_addr(&ti);
+ } else
+ sym = defobj->relocbase + def->st_value;
+ LD_UTRACE(UTRACE_DLSYM_STOP, handle, sym, 0, 0, name);
+ return (sym);
+ }
+
+ _rtld_error("Undefined symbol \"%s%s%s\"", name, ve != NULL ? "@" : "",
+ ve != NULL ? ve->name : "");
+ lock_release(rtld_bind_lock, &lockstate);
+ LD_UTRACE(UTRACE_DLSYM_STOP, handle, NULL, 0, 0, name);
+ return (NULL);
+}
+
+void *
+dlsym(void *handle, const char *name)
+{
+ return (do_dlsym(handle, name, __builtin_return_address(0), NULL,
+ SYMLOOK_DLSYM));
+}
+
+dlfunc_t
+dlfunc(void *handle, const char *name)
+{
+ union {
+ void *d;
+ dlfunc_t f;
+ } rv;
+
+ rv.d = do_dlsym(handle, name, __builtin_return_address(0), NULL,
+ SYMLOOK_DLSYM);
+ return (rv.f);
+}
+
+void *
+dlvsym(void *handle, const char *name, const char *version)
+{
+ Ver_Entry ventry;
+
+ ventry.name = version;
+ ventry.file = NULL;
+ ventry.hash = elf_hash(version);
+ ventry.flags = 0;
+ return (do_dlsym(handle, name, __builtin_return_address(0), &ventry,
+ SYMLOOK_DLSYM));
+}
+
+int
+_rtld_addr_phdr(const void *addr, struct dl_phdr_info *phdr_info)
+{
+ const Obj_Entry *obj;
+ RtldLockState lockstate;
+
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ obj = obj_from_addr(addr);
+ if (obj == NULL) {
+ _rtld_error("No shared object contains address");
+ lock_release(rtld_bind_lock, &lockstate);
+ return (0);
+ }
+ rtld_fill_dl_phdr_info(obj, phdr_info);
+ lock_release(rtld_bind_lock, &lockstate);
+ return (1);
+}
+
+int
+dladdr(const void *addr, Dl_info *info)
+{
+ const Obj_Entry *obj;
+ const Elf_Sym *def;
+ void *symbol_addr;
+ unsigned long symoffset;
+ RtldLockState lockstate;
+
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ obj = obj_from_addr(addr);
+ if (obj == NULL) {
+ _rtld_error("No shared object contains address");
+ lock_release(rtld_bind_lock, &lockstate);
+ return (0);
+ }
+ info->dli_fname = obj->path;
+ info->dli_fbase = obj->mapbase;
+ info->dli_saddr = (void *)0;
+ info->dli_sname = NULL;
+
+ /*
+ * Walk the symbol list looking for the symbol whose address is
+ * closest to the address sent in.
+ */
+ for (symoffset = 0; symoffset < obj->dynsymcount; symoffset++) {
+ def = obj->symtab + symoffset;
+
+ /*
+ * For skip the symbol if st_shndx is either SHN_UNDEF or
+ * SHN_COMMON.
+ */
+ if (def->st_shndx == SHN_UNDEF || def->st_shndx == SHN_COMMON)
+ continue;
+
+ /*
+ * If the symbol is greater than the specified address, or if it
+ * is further away from addr than the current nearest symbol,
+ * then reject it.
+ */
+ symbol_addr = obj->relocbase + def->st_value;
+ if (symbol_addr > addr || symbol_addr < info->dli_saddr)
+ continue;
+
+ /* Update our idea of the nearest symbol. */
+ info->dli_sname = obj->strtab + def->st_name;
+ info->dli_saddr = symbol_addr;
+
+ /* Exact match? */
+ if (info->dli_saddr == addr)
+ break;
+ }
+ lock_release(rtld_bind_lock, &lockstate);
+ return (1);
+}
+
+int
+dlinfo(void *handle, int request, void *p)
+{
+ const Obj_Entry *obj;
+ RtldLockState lockstate;
+ int error;
+
+ rlock_acquire(rtld_bind_lock, &lockstate);
+
+ if (handle == NULL || handle == RTLD_SELF) {
+ void *retaddr;
+
+ retaddr = __builtin_return_address(0); /* __GNUC__ only */
+ if ((obj = obj_from_addr(retaddr)) == NULL)
+ _rtld_error("Cannot determine caller's shared object");
+ } else
+ obj = dlcheck(handle);
+
+ if (obj == NULL) {
+ lock_release(rtld_bind_lock, &lockstate);
+ return (-1);
+ }
+
+ error = 0;
+ switch (request) {
+ case RTLD_DI_LINKMAP:
+ *((struct link_map const **)p) = &obj->linkmap;
+ break;
+ case RTLD_DI_ORIGIN:
+ error = rtld_dirname(obj->path, p);
+ break;
+
+ case RTLD_DI_SERINFOSIZE:
+ case RTLD_DI_SERINFO:
+ error = do_search_info(obj, request, (struct dl_serinfo *)p);
+ break;
+
+ default:
+ _rtld_error("Invalid request %d passed to dlinfo()", request);
+ error = -1;
+ }
+
+ lock_release(rtld_bind_lock, &lockstate);
+
+ return (error);
+}
+
+static void
+rtld_fill_dl_phdr_info(const Obj_Entry *obj, struct dl_phdr_info *phdr_info)
+{
+ phdr_info->dlpi_addr = (Elf_Addr)obj->relocbase;
+ phdr_info->dlpi_name = obj->path;
+ phdr_info->dlpi_phdr = obj->phdr;
+ phdr_info->dlpi_phnum = obj->phsize / sizeof(obj->phdr[0]);
+ phdr_info->dlpi_tls_modid = obj->tlsindex;
+ phdr_info->dlpi_tls_data = (char *)tls_get_addr_slow(_tcb_get(),
+ obj->tlsindex, 0, true);
+ phdr_info->dlpi_adds = obj_loads;
+ phdr_info->dlpi_subs = obj_loads - obj_count;
+}
+
+/*
+ * It's completely UB to actually use this, so extreme caution is advised. It's
+ * probably not what you want.
+ */
+int
+_dl_iterate_phdr_locked(__dl_iterate_hdr_callback callback, void *param)
+{
+ struct dl_phdr_info phdr_info;
+ Obj_Entry *obj;
+ int error;
+
+ for (obj = globallist_curr(TAILQ_FIRST(&obj_list)); obj != NULL;
+ obj = globallist_next(obj)) {
+ rtld_fill_dl_phdr_info(obj, &phdr_info);
+ error = callback(&phdr_info, sizeof(phdr_info), param);
+ if (error != 0)
+ return (error);
+ }
+
+ rtld_fill_dl_phdr_info(&obj_rtld, &phdr_info);
+ return (callback(&phdr_info, sizeof(phdr_info), param));
+}
+
+int
+dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param)
+{
+ struct dl_phdr_info phdr_info;
+ Obj_Entry *obj, marker;
+ RtldLockState bind_lockstate, phdr_lockstate;
+ int error;
+
+ init_marker(&marker);
+ error = 0;
+
+ wlock_acquire(rtld_phdr_lock, &phdr_lockstate);
+ wlock_acquire(rtld_bind_lock, &bind_lockstate);
+ for (obj = globallist_curr(TAILQ_FIRST(&obj_list)); obj != NULL;) {
+ TAILQ_INSERT_AFTER(&obj_list, obj, &marker, next);
+ rtld_fill_dl_phdr_info(obj, &phdr_info);
+ hold_object(obj);
+ lock_release(rtld_bind_lock, &bind_lockstate);
+
+ error = callback(&phdr_info, sizeof phdr_info, param);
+
+ wlock_acquire(rtld_bind_lock, &bind_lockstate);
+ unhold_object(obj);
+ obj = globallist_next(&marker);
+ TAILQ_REMOVE(&obj_list, &marker, next);
+ if (error != 0) {
+ lock_release(rtld_bind_lock, &bind_lockstate);
+ lock_release(rtld_phdr_lock, &phdr_lockstate);
+ return (error);
+ }
+ }
+
+ if (error == 0) {
+ rtld_fill_dl_phdr_info(&obj_rtld, &phdr_info);
+ lock_release(rtld_bind_lock, &bind_lockstate);
+ error = callback(&phdr_info, sizeof(phdr_info), param);
+ }
+ lock_release(rtld_phdr_lock, &phdr_lockstate);
+ return (error);
+}
+
+static void *
+fill_search_info(const char *dir, size_t dirlen, void *param)
+{
+ struct fill_search_info_args *arg;
+
+ arg = param;
+
+ if (arg->request == RTLD_DI_SERINFOSIZE) {
+ arg->serinfo->dls_cnt++;
+ arg->serinfo->dls_size += sizeof(struct dl_serpath) + dirlen +
+ 1;
+ } else {
+ struct dl_serpath *s_entry;
+
+ s_entry = arg->serpath;
+ s_entry->dls_name = arg->strspace;
+ s_entry->dls_flags = arg->flags;
+
+ strncpy(arg->strspace, dir, dirlen);
+ arg->strspace[dirlen] = '\0';
+
+ arg->strspace += dirlen + 1;
+ arg->serpath++;
+ }
+
+ return (NULL);
+}
+
+static int
+do_search_info(const Obj_Entry *obj, int request, struct dl_serinfo *info)
+{
+ struct dl_serinfo _info;
+ struct fill_search_info_args args;
+
+ args.request = RTLD_DI_SERINFOSIZE;
+ args.serinfo = &_info;
+
+ _info.dls_size = __offsetof(struct dl_serinfo, dls_serpath);
+ _info.dls_cnt = 0;
+
+ path_enumerate(obj->rpath, fill_search_info, NULL, &args);
+ path_enumerate(ld_library_path, fill_search_info, NULL, &args);
+ path_enumerate(obj->runpath, fill_search_info, NULL, &args);
+ path_enumerate(gethints(obj->z_nodeflib), fill_search_info, NULL,
+ &args);
+ if (!obj->z_nodeflib)
+ path_enumerate(ld_standard_library_path, fill_search_info, NULL,
+ &args);
+
+ if (request == RTLD_DI_SERINFOSIZE) {
+ info->dls_size = _info.dls_size;
+ info->dls_cnt = _info.dls_cnt;
+ return (0);
+ }
+
+ if (info->dls_cnt != _info.dls_cnt ||
+ info->dls_size != _info.dls_size) {
+ _rtld_error(
+ "Uninitialized Dl_serinfo struct passed to dlinfo()");
+ return (-1);
+ }
+
+ args.request = RTLD_DI_SERINFO;
+ args.serinfo = info;
+ args.serpath = &info->dls_serpath[0];
+ args.strspace = (char *)&info->dls_serpath[_info.dls_cnt];
+
+ args.flags = LA_SER_RUNPATH;
+ if (path_enumerate(obj->rpath, fill_search_info, NULL, &args) != NULL)
+ return (-1);
+
+ args.flags = LA_SER_LIBPATH;
+ if (path_enumerate(ld_library_path, fill_search_info, NULL, &args) !=
+ NULL)
+ return (-1);
+
+ args.flags = LA_SER_RUNPATH;
+ if (path_enumerate(obj->runpath, fill_search_info, NULL, &args) != NULL)
+ return (-1);
+
+ args.flags = LA_SER_CONFIG;
+ if (path_enumerate(gethints(obj->z_nodeflib), fill_search_info, NULL,
+ &args) != NULL)
+ return (-1);
+
+ args.flags = LA_SER_DEFAULT;
+ if (!obj->z_nodeflib &&
+ path_enumerate(ld_standard_library_path, fill_search_info, NULL,
+ &args) != NULL)
+ return (-1);
+ return (0);
+}
+
+static int
+rtld_dirname(const char *path, char *bname)
+{
+ const char *endp;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ bname[0] = '.';
+ bname[1] = '\0';
+ return (0);
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* Find the start of the dir */
+ while (endp > path && *endp != '/')
+ endp--;
+
+ /* Either the dir is "/" or there are no slashes */
+ if (endp == path) {
+ bname[0] = *endp == '/' ? '/' : '.';
+ bname[1] = '\0';
+ return (0);
+ } else {
+ do {
+ endp--;
+ } while (endp > path && *endp == '/');
+ }
+
+ if (endp - path + 2 > PATH_MAX) {
+ _rtld_error("Filename is too long: %s", path);
+ return (-1);
+ }
+
+ strncpy(bname, path, endp - path + 1);
+ bname[endp - path + 1] = '\0';
+ return (0);
+}
+
+static int
+rtld_dirname_abs(const char *path, char *base)
+{
+ char *last;
+
+ if (realpath(path, base) == NULL) {
+ _rtld_error("realpath \"%s\" failed (%s)", path,
+ rtld_strerror(errno));
+ return (-1);
+ }
+ dbg("%s -> %s", path, base);
+ last = strrchr(base, '/');
+ if (last == NULL) {
+ _rtld_error("non-abs result from realpath \"%s\"", path);
+ return (-1);
+ }
+ if (last != base)
+ *last = '\0';
+ return (0);
+}
+
+static void
+linkmap_add(Obj_Entry *obj)
+{
+ struct link_map *l, *prev;
+
+ l = &obj->linkmap;
+ l->l_name = obj->path;
+ l->l_base = obj->mapbase;
+ l->l_ld = obj->dynamic;
+ l->l_addr = obj->relocbase;
+
+ if (r_debug.r_map == NULL) {
+ r_debug.r_map = l;
+ return;
+ }
+
+ /*
+ * Scan to the end of the list, but not past the entry for the
+ * dynamic linker, which we want to keep at the very end.
+ */
+ for (prev = r_debug.r_map;
+ prev->l_next != NULL && prev->l_next != &obj_rtld.linkmap;
+ prev = prev->l_next)
+ ;
+
+ /* Link in the new entry. */
+ l->l_prev = prev;
+ l->l_next = prev->l_next;
+ if (l->l_next != NULL)
+ l->l_next->l_prev = l;
+ prev->l_next = l;
+}
+
+static void
+linkmap_delete(Obj_Entry *obj)
+{
+ struct link_map *l;
+
+ l = &obj->linkmap;
+ if (l->l_prev == NULL) {
+ if ((r_debug.r_map = l->l_next) != NULL)
+ l->l_next->l_prev = NULL;
+ return;
+ }
+
+ if ((l->l_prev->l_next = l->l_next) != NULL)
+ l->l_next->l_prev = l->l_prev;
+}
+
+/*
+ * Function for the debugger to set a breakpoint on to gain control.
+ *
+ * The two parameters allow the debugger to easily find and determine
+ * what the runtime loader is doing and to whom it is doing it.
+ *
+ * When the loadhook trap is hit (r_debug_state, set at program
+ * initialization), the arguments can be found on the stack:
+ *
+ * +8 struct link_map *m
+ * +4 struct r_debug *rd
+ * +0 RetAddr
+ */
+void
+r_debug_state(struct r_debug *rd __unused, struct link_map *m __unused)
+{
+ /*
+ * The following is a hack to force the compiler to emit calls to
+ * this function, even when optimizing. If the function is empty,
+ * the compiler is not obliged to emit any code for calls to it,
+ * even when marked __noinline. However, gdb depends on those
+ * calls being made.
+ */
+ __compiler_membar();
+}
+
+/*
+ * A function called after init routines have completed. This can be used to
+ * break before a program's entry routine is called, and can be used when
+ * main is not available in the symbol table.
+ */
+void
+_r_debug_postinit(struct link_map *m __unused)
+{
+ /* See r_debug_state(). */
+ __compiler_membar();
+}
+
+static void
+release_object(Obj_Entry *obj)
+{
+ if (obj->holdcount > 0) {
+ obj->unholdfree = true;
+ return;
+ }
+ munmap(obj->mapbase, obj->mapsize);
+ linkmap_delete(obj);
+ obj_free(obj);
+}
+
+/*
+ * Get address of the pointer variable in the main program.
+ * Prefer non-weak symbol over the weak one.
+ */
+static const void **
+get_program_var_addr(const char *name, RtldLockState *lockstate)
+{
+ SymLook req;
+ DoneList donelist;
+
+ symlook_init(&req, name);
+ req.lockstate = lockstate;
+ donelist_init(&donelist);
+ if (symlook_global(&req, &donelist) != 0)
+ return (NULL);
+ if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC)
+ return ((const void **)make_function_pointer(req.sym_out,
+ req.defobj_out));
+ else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC)
+ return ((const void **)rtld_resolve_ifunc(req.defobj_out,
+ req.sym_out));
+ else
+ return ((const void **)(req.defobj_out->relocbase +
+ req.sym_out->st_value));
+}
+
+/*
+ * Set a pointer variable in the main program to the given value. This
+ * is used to set key variables such as "environ" before any of the
+ * init functions are called.
+ */
+static void
+set_program_var(const char *name, const void *value)
+{
+ const void **addr;
+
+ if ((addr = get_program_var_addr(name, NULL)) != NULL) {
+ dbg("\"%s\": *%p <-- %p", name, addr, value);
+ *addr = value;
+ }
+}
+
+/*
+ * Search the global objects, including dependencies and main object,
+ * for the given symbol.
+ */
+static int
+symlook_global(SymLook *req, DoneList *donelist)
+{
+ SymLook req1;
+ const Objlist_Entry *elm;
+ int res;
+
+ symlook_init_from_req(&req1, req);
+
+ /* Search all objects loaded at program start up. */
+ if (req->defobj_out == NULL || (ld_dynamic_weak &&
+ ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK)) {
+ res = symlook_list(&req1, &list_main, donelist);
+ if (res == 0 && (!ld_dynamic_weak || req->defobj_out == NULL ||
+ ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ }
+
+ /* Search all DAGs whose roots are RTLD_GLOBAL objects. */
+ STAILQ_FOREACH(elm, &list_global, link) {
+ if (req->defobj_out != NULL && (!ld_dynamic_weak ||
+ ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK))
+ break;
+ res = symlook_list(&req1, &elm->obj->dagmembers, donelist);
+ if (res == 0 && (req->defobj_out == NULL ||
+ ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ }
+
+ return (req->sym_out != NULL ? 0 : ESRCH);
+}
+
+/*
+ * Given a symbol name in a referencing object, find the corresponding
+ * definition of the symbol. Returns a pointer to the symbol, or NULL if
+ * no definition was found. Returns a pointer to the Obj_Entry of the
+ * defining object via the reference parameter DEFOBJ_OUT.
+ */
+static int
+symlook_default(SymLook *req, const Obj_Entry *refobj)
+{
+ DoneList donelist;
+ const Objlist_Entry *elm;
+ SymLook req1;
+ int res;
+
+ donelist_init(&donelist);
+ symlook_init_from_req(&req1, req);
+
+ /*
+ * Look first in the referencing object if linked symbolically,
+ * and similarly handle protected symbols.
+ */
+ res = symlook_obj(&req1, refobj);
+ if (res == 0 && (refobj->symbolic ||
+ ELF_ST_VISIBILITY(req1.sym_out->st_other) == STV_PROTECTED ||
+ refobj->deepbind)) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ if (refobj->symbolic || req->defobj_out != NULL || refobj->deepbind)
+ donelist_check(&donelist, refobj);
+
+ if (!refobj->deepbind)
+ symlook_global(req, &donelist);
+
+ /* Search all dlopened DAGs containing the referencing object. */
+ STAILQ_FOREACH(elm, &refobj->dldags, link) {
+ if (req->sym_out != NULL && (!ld_dynamic_weak ||
+ ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK))
+ break;
+ res = symlook_list(&req1, &elm->obj->dagmembers, &donelist);
+ if (res == 0 && (req->sym_out == NULL ||
+ ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ }
+
+ if (refobj->deepbind)
+ symlook_global(req, &donelist);
+
+ /*
+ * Search the dynamic linker itself, and possibly resolve the
+ * symbol from there. This is how the application links to
+ * dynamic linker services such as dlopen.
+ */
+ if (req->sym_out == NULL ||
+ ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK) {
+ res = symlook_obj(&req1, &obj_rtld);
+ if (res == 0) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ }
+
+ return (req->sym_out != NULL ? 0 : ESRCH);
+}
+
+static int
+symlook_list(SymLook *req, const Objlist *objlist, DoneList *dlp)
+{
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ const Objlist_Entry *elm;
+ SymLook req1;
+ int res;
+
+ def = NULL;
+ defobj = NULL;
+ STAILQ_FOREACH(elm, objlist, link) {
+ if (donelist_check(dlp, elm->obj))
+ continue;
+ symlook_init_from_req(&req1, req);
+ if ((res = symlook_obj(&req1, elm->obj)) == 0) {
+ if (def == NULL || (ld_dynamic_weak &&
+ ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+ def = req1.sym_out;
+ defobj = req1.defobj_out;
+ if (!ld_dynamic_weak ||
+ ELF_ST_BIND(def->st_info) != STB_WEAK)
+ break;
+ }
+ }
+ }
+ if (def != NULL) {
+ req->sym_out = def;
+ req->defobj_out = defobj;
+ return (0);
+ }
+ return (ESRCH);
+}
+
+/*
+ * Search the chain of DAGS cointed to by the given Needed_Entry
+ * for a symbol of the given name. Each DAG is scanned completely
+ * before advancing to the next one. Returns a pointer to the symbol,
+ * or NULL if no definition was found.
+ */
+static int
+symlook_needed(SymLook *req, const Needed_Entry *needed, DoneList *dlp)
+{
+ const Elf_Sym *def;
+ const Needed_Entry *n;
+ const Obj_Entry *defobj;
+ SymLook req1;
+ int res;
+
+ def = NULL;
+ defobj = NULL;
+ symlook_init_from_req(&req1, req);
+ for (n = needed; n != NULL; n = n->next) {
+ if (n->obj == NULL || (res = symlook_list(&req1,
+ &n->obj->dagmembers, dlp)) != 0)
+ continue;
+ if (def == NULL || (ld_dynamic_weak &&
+ ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+ def = req1.sym_out;
+ defobj = req1.defobj_out;
+ if (!ld_dynamic_weak ||
+ ELF_ST_BIND(def->st_info) != STB_WEAK)
+ break;
+ }
+ }
+ if (def != NULL) {
+ req->sym_out = def;
+ req->defobj_out = defobj;
+ return (0);
+ }
+ return (ESRCH);
+}
+
+static int
+symlook_obj_load_filtees(SymLook *req, SymLook *req1, const Obj_Entry *obj,
+ Needed_Entry *needed)
+{
+ DoneList donelist;
+ int flags;
+
+ flags = (req->flags & SYMLOOK_EARLY) != 0 ? RTLD_LO_EARLY : 0;
+ load_filtees(__DECONST(Obj_Entry *, obj), flags, req->lockstate);
+ donelist_init(&donelist);
+ symlook_init_from_req(req1, req);
+ return (symlook_needed(req1, needed, &donelist));
+}
+
+/*
+ * Search the symbol table of a single shared object for a symbol of
+ * the given name and version, if requested. Returns a pointer to the
+ * symbol, or NULL if no definition was found. If the object is
+ * filter, return filtered symbol from filtee.
+ *
+ * The symbol's hash value is passed in for efficiency reasons; that
+ * eliminates many recomputations of the hash value.
+ */
+int
+symlook_obj(SymLook *req, const Obj_Entry *obj)
+{
+ SymLook req1;
+ int res, mres;
+
+ /*
+ * If there is at least one valid hash at this point, we prefer to
+ * use the faster GNU version if available.
+ */
+ if (obj->valid_hash_gnu)
+ mres = symlook_obj1_gnu(req, obj);
+ else if (obj->valid_hash_sysv)
+ mres = symlook_obj1_sysv(req, obj);
+ else
+ return (EINVAL);
+
+ if (mres == 0) {
+ if (obj->needed_filtees != NULL) {
+ res = symlook_obj_load_filtees(req, &req1, obj,
+ obj->needed_filtees);
+ if (res == 0) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ }
+ return (res);
+ }
+ if (obj->needed_aux_filtees != NULL) {
+ res = symlook_obj_load_filtees(req, &req1, obj,
+ obj->needed_aux_filtees);
+ if (res == 0) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ return (res);
+ }
+ }
+ }
+ return (mres);
+}
+
+/* Symbol match routine common to both hash functions */
+static bool
+matched_symbol(SymLook *req, const Obj_Entry *obj, Sym_Match_Result *result,
+ const unsigned long symnum)
+{
+ Elf_Versym verndx;
+ const Elf_Sym *symp;
+ const char *strp;
+
+ symp = obj->symtab + symnum;
+ strp = obj->strtab + symp->st_name;
+
+ switch (ELF_ST_TYPE(symp->st_info)) {
+ case STT_FUNC:
+ case STT_NOTYPE:
+ case STT_OBJECT:
+ case STT_COMMON:
+ case STT_GNU_IFUNC:
+ if (symp->st_value == 0)
+ return (false);
+ /* fallthrough */
+ case STT_TLS:
+ if (symp->st_shndx != SHN_UNDEF)
+ break;
+ else if (((req->flags & SYMLOOK_IN_PLT) == 0) &&
+ (ELF_ST_TYPE(symp->st_info) == STT_FUNC))
+ break;
+ /* fallthrough */
+ default:
+ return (false);
+ }
+ if (req->name[0] != strp[0] || strcmp(req->name, strp) != 0)
+ return (false);
+
+ if (req->ventry == NULL) {
+ if (obj->versyms != NULL) {
+ verndx = VER_NDX(obj->versyms[symnum]);
+ if (verndx > obj->vernum) {
+ _rtld_error(
+ "%s: symbol %s references wrong version %d",
+ obj->path, obj->strtab + symnum, verndx);
+ return (false);
+ }
+ /*
+ * If we are not called from dlsym (i.e. this
+ * is a normal relocation from unversioned
+ * binary), accept the symbol immediately if
+ * it happens to have first version after this
+ * shared object became versioned. Otherwise,
+ * if symbol is versioned and not hidden,
+ * remember it. If it is the only symbol with
+ * this name exported by the shared object, it
+ * will be returned as a match by the calling
+ * function. If symbol is global (verndx < 2)
+ * accept it unconditionally.
+ */
+ if ((req->flags & SYMLOOK_DLSYM) == 0 &&
+ verndx == VER_NDX_GIVEN) {
+ result->sym_out = symp;
+ return (true);
+ } else if (verndx >= VER_NDX_GIVEN) {
+ if ((obj->versyms[symnum] & VER_NDX_HIDDEN) ==
+ 0) {
+ if (result->vsymp == NULL)
+ result->vsymp = symp;
+ result->vcount++;
+ }
+ return (false);
+ }
+ }
+ result->sym_out = symp;
+ return (true);
+ }
+ if (obj->versyms == NULL) {
+ if (object_match_name(obj, req->ventry->name)) {
+ _rtld_error(
+ "%s: object %s should provide version %s for symbol %s",
+ obj_rtld.path, obj->path, req->ventry->name,
+ obj->strtab + symnum);
+ return (false);
+ }
+ } else {
+ verndx = VER_NDX(obj->versyms[symnum]);
+ if (verndx > obj->vernum) {
+ _rtld_error("%s: symbol %s references wrong version %d",
+ obj->path, obj->strtab + symnum, verndx);
+ return (false);
+ }
+ if (obj->vertab[verndx].hash != req->ventry->hash ||
+ strcmp(obj->vertab[verndx].name, req->ventry->name)) {
+ /*
+ * Version does not match. Look if this is a
+ * global symbol and if it is not hidden. If
+ * global symbol (verndx < 2) is available,
+ * use it. Do not return symbol if we are
+ * called by dlvsym, because dlvsym looks for
+ * a specific version and default one is not
+ * what dlvsym wants.
+ */
+ if ((req->flags & SYMLOOK_DLSYM) ||
+ (verndx >= VER_NDX_GIVEN) ||
+ (obj->versyms[symnum] & VER_NDX_HIDDEN))
+ return (false);
+ }
+ }
+ result->sym_out = symp;
+ return (true);
+}
+
+/*
+ * Search for symbol using SysV hash function.
+ * obj->buckets is known not to be NULL at this point; the test for this was
+ * performed with the obj->valid_hash_sysv assignment.
+ */
+static int
+symlook_obj1_sysv(SymLook *req, const Obj_Entry *obj)
+{
+ unsigned long symnum;
+ Sym_Match_Result matchres;
+
+ matchres.sym_out = NULL;
+ matchres.vsymp = NULL;
+ matchres.vcount = 0;
+
+ for (symnum = obj->buckets[req->hash % obj->nbuckets];
+ symnum != STN_UNDEF; symnum = obj->chains[symnum]) {
+ if (symnum >= obj->nchains)
+ return (ESRCH); /* Bad object */
+
+ if (matched_symbol(req, obj, &matchres, symnum)) {
+ req->sym_out = matchres.sym_out;
+ req->defobj_out = obj;
+ return (0);
+ }
+ }
+ if (matchres.vcount == 1) {
+ req->sym_out = matchres.vsymp;
+ req->defobj_out = obj;
+ return (0);
+ }
+ return (ESRCH);
+}
+
+/* Search for symbol using GNU hash function */
+static int
+symlook_obj1_gnu(SymLook *req, const Obj_Entry *obj)
+{
+ Elf_Addr bloom_word;
+ const Elf32_Word *hashval;
+ Elf32_Word bucket;
+ Sym_Match_Result matchres;
+ unsigned int h1, h2;
+ unsigned long symnum;
+
+ matchres.sym_out = NULL;
+ matchres.vsymp = NULL;
+ matchres.vcount = 0;
+
+ /* Pick right bitmask word from Bloom filter array */
+ bloom_word = obj->bloom_gnu[(req->hash_gnu / __ELF_WORD_SIZE) &
+ obj->maskwords_bm_gnu];
+
+ /* Calculate modulus word size of gnu hash and its derivative */
+ h1 = req->hash_gnu & (__ELF_WORD_SIZE - 1);
+ h2 = ((req->hash_gnu >> obj->shift2_gnu) & (__ELF_WORD_SIZE - 1));
+
+ /* Filter out the "definitely not in set" queries */
+ if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0)
+ return (ESRCH);
+
+ /* Locate hash chain and corresponding value element*/
+ bucket = obj->buckets_gnu[req->hash_gnu % obj->nbuckets_gnu];
+ if (bucket == 0)
+ return (ESRCH);
+ hashval = &obj->chain_zero_gnu[bucket];
+ do {
+ if (((*hashval ^ req->hash_gnu) >> 1) == 0) {
+ symnum = hashval - obj->chain_zero_gnu;
+ if (matched_symbol(req, obj, &matchres, symnum)) {
+ req->sym_out = matchres.sym_out;
+ req->defobj_out = obj;
+ return (0);
+ }
+ }
+ } while ((*hashval++ & 1) == 0);
+ if (matchres.vcount == 1) {
+ req->sym_out = matchres.vsymp;
+ req->defobj_out = obj;
+ return (0);
+ }
+ return (ESRCH);
+}
+
+static void
+trace_calc_fmts(const char **main_local, const char **fmt1, const char **fmt2)
+{
+ *main_local = ld_get_env_var(LD_TRACE_LOADED_OBJECTS_PROGNAME);
+ if (*main_local == NULL)
+ *main_local = "";
+
+ *fmt1 = ld_get_env_var(LD_TRACE_LOADED_OBJECTS_FMT1);
+ if (*fmt1 == NULL)
+ *fmt1 = "\t%o => %p (%x)\n";
+
+ *fmt2 = ld_get_env_var(LD_TRACE_LOADED_OBJECTS_FMT2);
+ if (*fmt2 == NULL)
+ *fmt2 = "\t%o (%x)\n";
+}
+
+static void
+trace_print_obj(Obj_Entry *obj, const char *name, const char *path,
+ const char *main_local, const char *fmt1, const char *fmt2)
+{
+ const char *fmt;
+ int c;
+
+ if (fmt1 == NULL)
+ fmt = fmt2;
+ else
+ /* XXX bogus */
+ fmt = strncmp(name, "lib", 3) == 0 ? fmt1 : fmt2;
+
+ while ((c = *fmt++) != '\0') {
+ switch (c) {
+ default:
+ rtld_putchar(c);
+ continue;
+ case '\\':
+ switch (c = *fmt) {
+ case '\0':
+ continue;
+ case 'n':
+ rtld_putchar('\n');
+ break;
+ case 't':
+ rtld_putchar('\t');
+ break;
+ }
+ break;
+ case '%':
+ switch (c = *fmt) {
+ case '\0':
+ continue;
+ case '%':
+ default:
+ rtld_putchar(c);
+ break;
+ case 'A':
+ rtld_putstr(main_local);
+ break;
+ case 'a':
+ rtld_putstr(obj_main->path);
+ break;
+ case 'o':
+ rtld_putstr(name);
+ break;
+ case 'p':
+ rtld_putstr(path);
+ break;
+ case 'x':
+ rtld_printf("%p",
+ obj != NULL ? obj->mapbase : NULL);
+ break;
+ }
+ break;
+ }
+ ++fmt;
+ }
+}
+
+static void
+trace_loaded_objects(Obj_Entry *obj, bool show_preload)
+{
+ const char *fmt1, *fmt2, *main_local;
+ const char *name, *path;
+ bool first_spurious, list_containers;
+
+ trace_calc_fmts(&main_local, &fmt1, &fmt2);
+ list_containers = ld_get_env_var(LD_TRACE_LOADED_OBJECTS_ALL) != NULL;
+
+ for (; obj != NULL; obj = TAILQ_NEXT(obj, next)) {
+ Needed_Entry *needed;
+
+ if (obj->marker)
+ continue;
+ if (list_containers && obj->needed != NULL)
+ rtld_printf("%s:\n", obj->path);
+ for (needed = obj->needed; needed; needed = needed->next) {
+ if (needed->obj != NULL) {
+ if (needed->obj->traced && !list_containers)
+ continue;
+ needed->obj->traced = true;
+ path = needed->obj->path;
+ } else
+ path = "not found";
+
+ name = obj->strtab + needed->name;
+ trace_print_obj(needed->obj, name, path, main_local,
+ fmt1, fmt2);
+ }
+ }
+
+ if (show_preload) {
+ if (ld_get_env_var(LD_TRACE_LOADED_OBJECTS_FMT2) == NULL)
+ fmt2 = "\t%p (%x)\n";
+ first_spurious = true;
+
+ TAILQ_FOREACH(obj, &obj_list, next) {
+ if (obj->marker || obj == obj_main || obj->traced)
+ continue;
+
+ if (list_containers && first_spurious) {
+ rtld_printf("[preloaded]\n");
+ first_spurious = false;
+ }
+
+ Name_Entry *fname = STAILQ_FIRST(&obj->names);
+ name = fname == NULL ? "<unknown>" : fname->name;
+ trace_print_obj(obj, name, obj->path, main_local, NULL,
+ fmt2);
+ }
+ }
+}
+
+/*
+ * Unload a dlopened object and its dependencies from memory and from
+ * our data structures. It is assumed that the DAG rooted in the
+ * object has already been unreferenced, and that the object has a
+ * reference count of 0.
+ */
+static void
+unload_object(Obj_Entry *root, RtldLockState *lockstate)
+{
+ Obj_Entry marker, *obj, *next;
+
+ assert(root->refcount == 0);
+
+ /*
+ * Pass over the DAG removing unreferenced objects from
+ * appropriate lists.
+ */
+ unlink_object(root);
+
+ /* Unmap all objects that are no longer referenced. */
+ for (obj = TAILQ_FIRST(&obj_list); obj != NULL; obj = next) {
+ next = TAILQ_NEXT(obj, next);
+ if (obj->marker || obj->refcount != 0)
+ continue;
+ LD_UTRACE(UTRACE_UNLOAD_OBJECT, obj, obj->mapbase, obj->mapsize,
+ 0, obj->path);
+ dbg("unloading \"%s\"", obj->path);
+ /*
+ * Unlink the object now to prevent new references from
+ * being acquired while the bind lock is dropped in
+ * recursive dlclose() invocations.
+ */
+ TAILQ_REMOVE(&obj_list, obj, next);
+ obj_count--;
+
+ if (obj->filtees_loaded) {
+ if (next != NULL) {
+ init_marker(&marker);
+ TAILQ_INSERT_BEFORE(next, &marker, next);
+ unload_filtees(obj, lockstate);
+ next = TAILQ_NEXT(&marker, next);
+ TAILQ_REMOVE(&obj_list, &marker, next);
+ } else
+ unload_filtees(obj, lockstate);
+ }
+ release_object(obj);
+ }
+}
+
+static void
+unlink_object(Obj_Entry *root)
+{
+ Objlist_Entry *elm;
+
+ if (root->refcount == 0) {
+ /* Remove the object from the RTLD_GLOBAL list. */
+ objlist_remove(&list_global, root);
+
+ /* Remove the object from all objects' DAG lists. */
+ STAILQ_FOREACH(elm, &root->dagmembers, link) {
+ objlist_remove(&elm->obj->dldags, root);
+ if (elm->obj != root)
+ unlink_object(elm->obj);
+ }
+ }
+}
+
+static void
+ref_dag(Obj_Entry *root)
+{
+ Objlist_Entry *elm;
+
+ assert(root->dag_inited);
+ STAILQ_FOREACH(elm, &root->dagmembers, link)
+ elm->obj->refcount++;
+}
+
+static void
+unref_dag(Obj_Entry *root)
+{
+ Objlist_Entry *elm;
+
+ assert(root->dag_inited);
+ STAILQ_FOREACH(elm, &root->dagmembers, link)
+ elm->obj->refcount--;
+}
+
+/*
+ * Common code for MD __tls_get_addr().
+ */
+static void *
+tls_get_addr_slow(struct tcb *tcb, int index, size_t offset, bool locked)
+{
+ struct dtv *newdtv, *dtv;
+ RtldLockState lockstate;
+ int to_copy;
+
+ dtv = tcb->tcb_dtv;
+ /* Check dtv generation in case new modules have arrived */
+ if (dtv->dtv_gen != tls_dtv_generation) {
+ if (!locked)
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ newdtv = xcalloc(1, sizeof(struct dtv) + tls_max_index *
+ sizeof(struct dtv_slot));
+ to_copy = dtv->dtv_size;
+ if (to_copy > tls_max_index)
+ to_copy = tls_max_index;
+ memcpy(newdtv->dtv_slots, dtv->dtv_slots, to_copy *
+ sizeof(struct dtv_slot));
+ newdtv->dtv_gen = tls_dtv_generation;
+ newdtv->dtv_size = tls_max_index;
+ free(dtv);
+ if (!locked)
+ lock_release(rtld_bind_lock, &lockstate);
+ dtv = tcb->tcb_dtv = newdtv;
+ }
+
+ /* Dynamically allocate module TLS if necessary */
+ if (dtv->dtv_slots[index - 1].dtvs_tls == 0) {
+ /* Signal safe, wlock will block out signals. */
+ if (!locked)
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ if (!dtv->dtv_slots[index - 1].dtvs_tls)
+ dtv->dtv_slots[index - 1].dtvs_tls =
+ allocate_module_tls(tcb, index);
+ if (!locked)
+ lock_release(rtld_bind_lock, &lockstate);
+ }
+ return (dtv->dtv_slots[index - 1].dtvs_tls + offset);
+}
+
+void *
+tls_get_addr_common(struct tcb *tcb, int index, size_t offset)
+{
+ struct dtv *dtv;
+
+ dtv = tcb->tcb_dtv;
+ /* Check dtv generation in case new modules have arrived */
+ if (__predict_true(dtv->dtv_gen == tls_dtv_generation &&
+ dtv->dtv_slots[index - 1].dtvs_tls != 0))
+ return (dtv->dtv_slots[index - 1].dtvs_tls + offset);
+ return (tls_get_addr_slow(tcb, index, offset, false));
+}
+
+static struct tcb *
+tcb_from_tcb_list_entry(struct tcb_list_entry *tcbelm)
+{
+#ifdef TLS_VARIANT_I
+ return ((struct tcb *)((char *)tcbelm - tcb_list_entry_offset));
+#else
+ return ((struct tcb *)((char *)tcbelm + tcb_list_entry_offset));
+#endif
+}
+
+static struct tcb_list_entry *
+tcb_list_entry_from_tcb(struct tcb *tcb)
+{
+#ifdef TLS_VARIANT_I
+ return ((struct tcb_list_entry *)((char *)tcb + tcb_list_entry_offset));
+#else
+ return ((struct tcb_list_entry *)((char *)tcb - tcb_list_entry_offset));
+#endif
+}
+
+static void
+tcb_list_insert(struct tcb *tcb)
+{
+ struct tcb_list_entry *tcbelm;
+
+ tcbelm = tcb_list_entry_from_tcb(tcb);
+ TAILQ_INSERT_TAIL(&tcb_list, tcbelm, next);
+}
+
+static void
+tcb_list_remove(struct tcb *tcb)
+{
+ struct tcb_list_entry *tcbelm;
+
+ tcbelm = tcb_list_entry_from_tcb(tcb);
+ TAILQ_REMOVE(&tcb_list, tcbelm, next);
+}
+
+#ifdef TLS_VARIANT_I
+
+/*
+ * Return pointer to allocated TLS block
+ */
+static void *
+get_tls_block_ptr(void *tcb, size_t tcbsize)
+{
+ size_t extra_size, post_size, pre_size, tls_block_size;
+ size_t tls_init_align;
+
+ tls_init_align = MAX(obj_main->tlsalign, 1);
+
+ /* Compute fragments sizes. */
+ extra_size = tcbsize - TLS_TCB_SIZE;
+ post_size = calculate_tls_post_size(tls_init_align);
+ tls_block_size = tcbsize + post_size;
+ pre_size = roundup2(tls_block_size, tls_init_align) - tls_block_size;
+
+ return ((char *)tcb - pre_size - extra_size);
+}
+
+/*
+ * Allocate Static TLS using the Variant I method.
+ *
+ * For details on the layout, see lib/libc/gen/tls.c.
+ *
+ * NB: rtld's tls_static_space variable includes TLS_TCB_SIZE and post_size as
+ * it is based on tls_last_offset, and TLS offsets here are really TCB
+ * offsets, whereas libc's tls_static_space is just the executable's static
+ * TLS segment.
+ *
+ * NB: This differs from NetBSD's ld.elf_so, where TLS offsets are relative to
+ * the end of the TCB.
+ */
+void *
+allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign)
+{
+ Obj_Entry *obj;
+ char *tls_block;
+ struct dtv *dtv;
+ struct tcb *tcb;
+ char *addr;
+ size_t i;
+ size_t extra_size, maxalign, post_size, pre_size, tls_block_size;
+ size_t tls_init_align, tls_init_offset;
+
+ if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
+ return (oldtcb);
+
+ assert(tcbsize >= TLS_TCB_SIZE);
+ maxalign = MAX(tcbalign, tls_static_max_align);
+ tls_init_align = MAX(obj_main->tlsalign, 1);
+
+ /* Compute fragments sizes. */
+ extra_size = tcbsize - TLS_TCB_SIZE;
+ post_size = calculate_tls_post_size(tls_init_align);
+ tls_block_size = tcbsize + post_size;
+ pre_size = roundup2(tls_block_size, tls_init_align) - tls_block_size;
+ tls_block_size += pre_size + tls_static_space - TLS_TCB_SIZE -
+ post_size;
+
+ /* Allocate whole TLS block */
+ tls_block = xmalloc_aligned(tls_block_size, maxalign, 0);
+ tcb = (struct tcb *)(tls_block + pre_size + extra_size);
+
+ if (oldtcb != NULL) {
+ memcpy(tls_block, get_tls_block_ptr(oldtcb, tcbsize),
+ tls_static_space);
+ free(get_tls_block_ptr(oldtcb, tcbsize));
+
+ /* Adjust the DTV. */
+ dtv = tcb->tcb_dtv;
+ for (i = 0; i < dtv->dtv_size; i++) {
+ if ((uintptr_t)dtv->dtv_slots[i].dtvs_tls >=
+ (uintptr_t)oldtcb &&
+ (uintptr_t)dtv->dtv_slots[i].dtvs_tls <
+ (uintptr_t)oldtcb + tls_static_space) {
+ dtv->dtv_slots[i].dtvs_tls = (char *)tcb +
+ (dtv->dtv_slots[i].dtvs_tls -
+ (char *)oldtcb);
+ }
+ }
+ } else {
+ dtv = xcalloc(1, sizeof(struct dtv) + tls_max_index *
+ sizeof(struct dtv_slot));
+ tcb->tcb_dtv = dtv;
+ dtv->dtv_gen = tls_dtv_generation;
+ dtv->dtv_size = tls_max_index;
+
+ for (obj = globallist_curr(objs); obj != NULL;
+ obj = globallist_next(obj)) {
+ if (obj->tlsoffset == 0)
+ continue;
+ tls_init_offset = obj->tlspoffset & (obj->tlsalign - 1);
+ addr = (char *)tcb + obj->tlsoffset;
+ if (tls_init_offset > 0)
+ memset(addr, 0, tls_init_offset);
+ if (obj->tlsinitsize > 0) {
+ memcpy(addr + tls_init_offset, obj->tlsinit,
+ obj->tlsinitsize);
+ }
+ if (obj->tlssize > obj->tlsinitsize) {
+ memset(addr + tls_init_offset +
+ obj->tlsinitsize,
+ 0,
+ obj->tlssize - obj->tlsinitsize -
+ tls_init_offset);
+ }
+ dtv->dtv_slots[obj->tlsindex - 1].dtvs_tls = addr;
+ }
+ }
+
+ tcb_list_insert(tcb);
+ return (tcb);
+}
+
+void
+free_tls(void *tcb, size_t tcbsize, size_t tcbalign __unused)
+{
+ struct dtv *dtv;
+ uintptr_t tlsstart, tlsend;
+ size_t post_size;
+ size_t i, tls_init_align __unused;
+
+ tcb_list_remove(tcb);
+
+ assert(tcbsize >= TLS_TCB_SIZE);
+ tls_init_align = MAX(obj_main->tlsalign, 1);
+
+ /* Compute fragments sizes. */
+ post_size = calculate_tls_post_size(tls_init_align);
+
+ tlsstart = (uintptr_t)tcb + TLS_TCB_SIZE + post_size;
+ tlsend = (uintptr_t)tcb + tls_static_space;
+
+ dtv = ((struct tcb *)tcb)->tcb_dtv;
+ for (i = 0; i < dtv->dtv_size; i++) {
+ if (dtv->dtv_slots[i].dtvs_tls != NULL &&
+ ((uintptr_t)dtv->dtv_slots[i].dtvs_tls < tlsstart ||
+ (uintptr_t)dtv->dtv_slots[i].dtvs_tls >= tlsend)) {
+ free(dtv->dtv_slots[i].dtvs_tls);
+ }
+ }
+ free(dtv);
+ free(get_tls_block_ptr(tcb, tcbsize));
+}
+
+#endif /* TLS_VARIANT_I */
+
+#ifdef TLS_VARIANT_II
+
+/*
+ * Allocate Static TLS using the Variant II method.
+ */
+void *
+allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign)
+{
+ Obj_Entry *obj;
+ size_t size, ralign;
+ char *tls_block;
+ struct dtv *dtv, *olddtv;
+ struct tcb *tcb;
+ char *addr;
+ size_t i;
+
+ ralign = tcbalign;
+ if (tls_static_max_align > ralign)
+ ralign = tls_static_max_align;
+ size = roundup(tls_static_space, ralign) + roundup(tcbsize, ralign);
+
+ assert(tcbsize >= 2 * sizeof(uintptr_t));
+ tls_block = xmalloc_aligned(size, ralign, 0 /* XXX */);
+ dtv = xcalloc(1, sizeof(struct dtv) + tls_max_index *
+ sizeof(struct dtv_slot));
+
+ tcb = (struct tcb *)(tls_block + roundup(tls_static_space, ralign));
+ tcb->tcb_self = tcb;
+ tcb->tcb_dtv = dtv;
+
+ dtv->dtv_gen = tls_dtv_generation;
+ dtv->dtv_size = tls_max_index;
+
+ if (oldtcb != NULL) {
+ /*
+ * Copy the static TLS block over whole.
+ */
+ memcpy((char *)tcb - tls_static_space,
+ (const char *)oldtcb - tls_static_space,
+ tls_static_space);
+
+ /*
+ * If any dynamic TLS blocks have been created tls_get_addr(),
+ * move them over.
+ */
+ olddtv = ((struct tcb *)oldtcb)->tcb_dtv;
+ for (i = 0; i < olddtv->dtv_size; i++) {
+ if ((uintptr_t)olddtv->dtv_slots[i].dtvs_tls <
+ (uintptr_t)oldtcb - size ||
+ (uintptr_t)olddtv->dtv_slots[i].dtvs_tls >
+ (uintptr_t)oldtcb) {
+ dtv->dtv_slots[i].dtvs_tls =
+ olddtv->dtv_slots[i].dtvs_tls;
+ olddtv->dtv_slots[i].dtvs_tls = NULL;
+ }
+ }
+
+ /*
+ * We assume that this block was the one we created with
+ * allocate_initial_tls().
+ */
+ free_tls(oldtcb, 2 * sizeof(uintptr_t), sizeof(uintptr_t));
+ } else {
+ for (obj = objs; obj != NULL; obj = TAILQ_NEXT(obj, next)) {
+ if (obj->marker || obj->tlsoffset == 0)
+ continue;
+ addr = (char *)tcb - obj->tlsoffset;
+ memset(addr + obj->tlsinitsize, 0, obj->tlssize -
+ obj->tlsinitsize);
+ if (obj->tlsinit) {
+ memcpy(addr, obj->tlsinit, obj->tlsinitsize);
+ obj->static_tls_copied = true;
+ }
+ dtv->dtv_slots[obj->tlsindex - 1].dtvs_tls = addr;
+ }
+ }
+
+ tcb_list_insert(tcb);
+ return (tcb);
+}
+
+void
+free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign)
+{
+ struct dtv *dtv;
+ size_t size, ralign;
+ size_t i;
+ uintptr_t tlsstart, tlsend;
+
+ tcb_list_remove(tcb);
+
+ /*
+ * Figure out the size of the initial TLS block so that we can
+ * find stuff which ___tls_get_addr() allocated dynamically.
+ */
+ ralign = tcbalign;
+ if (tls_static_max_align > ralign)
+ ralign = tls_static_max_align;
+ size = roundup(tls_static_space, ralign);
+
+ dtv = ((struct tcb *)tcb)->tcb_dtv;
+ tlsend = (uintptr_t)tcb;
+ tlsstart = tlsend - size;
+ for (i = 0; i < dtv->dtv_size; i++) {
+ if (dtv->dtv_slots[i].dtvs_tls != NULL &&
+ ((uintptr_t)dtv->dtv_slots[i].dtvs_tls < tlsstart ||
+ (uintptr_t)dtv->dtv_slots[i].dtvs_tls > tlsend)) {
+ free(dtv->dtv_slots[i].dtvs_tls);
+ }
+ }
+
+ free((void *)tlsstart);
+ free(dtv);
+}
+
+#endif /* TLS_VARIANT_II */
+
+/*
+ * Allocate TLS block for module with given index.
+ */
+void *
+allocate_module_tls(struct tcb *tcb, int index)
+{
+ Obj_Entry *obj;
+ char *p;
+
+ TAILQ_FOREACH(obj, &obj_list, next) {
+ if (obj->marker)
+ continue;
+ if (obj->tlsindex == index)
+ break;
+ }
+ if (obj == NULL) {
+ _rtld_error("Can't find module with TLS index %d", index);
+ rtld_die();
+ }
+
+ if (obj->tls_static) {
+#ifdef TLS_VARIANT_I
+ p = (char *)tcb + obj->tlsoffset;
+#else
+ p = (char *)tcb - obj->tlsoffset;
+#endif
+ return (p);
+ }
+
+ obj->tls_dynamic = true;
+
+ p = xmalloc_aligned(obj->tlssize, obj->tlsalign, obj->tlspoffset);
+ memcpy(p, obj->tlsinit, obj->tlsinitsize);
+ memset(p + obj->tlsinitsize, 0, obj->tlssize - obj->tlsinitsize);
+ return (p);
+}
+
+static bool
+allocate_tls_offset_common(size_t *offp, size_t tlssize, size_t tlsalign,
+ size_t tlspoffset __unused)
+{
+ size_t off;
+
+ if (tls_last_offset == 0)
+ off = calculate_first_tls_offset(tlssize, tlsalign,
+ tlspoffset);
+ else
+ off = calculate_tls_offset(tls_last_offset, tls_last_size,
+ tlssize, tlsalign, tlspoffset);
+
+ *offp = off;
+#ifdef TLS_VARIANT_I
+ off += tlssize;
+#endif
+
+ /*
+ * If we have already fixed the size of the static TLS block, we
+ * must stay within that size. When allocating the static TLS, we
+ * leave a small amount of space spare to be used for dynamically
+ * loading modules which use static TLS.
+ */
+ if (tls_static_space != 0) {
+ if (off > tls_static_space)
+ return (false);
+ } else if (tlsalign > tls_static_max_align) {
+ tls_static_max_align = tlsalign;
+ }
+
+ tls_last_offset = off;
+ tls_last_size = tlssize;
+
+ return (true);
+}
+
+bool
+allocate_tls_offset(Obj_Entry *obj)
+{
+ if (obj->tls_dynamic)
+ return (false);
+
+ if (obj->tls_static)
+ return (true);
+
+ if (obj->tlssize == 0) {
+ obj->tls_static = true;
+ return (true);
+ }
+
+ if (!allocate_tls_offset_common(&obj->tlsoffset, obj->tlssize,
+ obj->tlsalign, obj->tlspoffset))
+ return (false);
+
+ obj->tls_static = true;
+
+ return (true);
+}
+
+void
+free_tls_offset(Obj_Entry *obj)
+{
+ /*
+ * If we were the last thing to allocate out of the static TLS
+ * block, we give our space back to the 'allocator'. This is a
+ * simplistic workaround to allow libGL.so.1 to be loaded and
+ * unloaded multiple times.
+ */
+ size_t off = obj->tlsoffset;
+
+#ifdef TLS_VARIANT_I
+ off += obj->tlssize;
+#endif
+ if (off == tls_last_offset) {
+ tls_last_offset -= obj->tlssize;
+ tls_last_size = 0;
+ }
+}
+
+void *
+_rtld_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign)
+{
+ void *ret;
+ RtldLockState lockstate;
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ ret = allocate_tls(globallist_curr(TAILQ_FIRST(&obj_list)), oldtcb,
+ tcbsize, tcbalign);
+ lock_release(rtld_bind_lock, &lockstate);
+ return (ret);
+}
+
+void
+_rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
+{
+ RtldLockState lockstate;
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ free_tls(tcb, tcbsize, tcbalign);
+ lock_release(rtld_bind_lock, &lockstate);
+}
+
+static void
+object_add_name(Obj_Entry *obj, const char *name)
+{
+ Name_Entry *entry;
+ size_t len;
+
+ len = strlen(name);
+ entry = malloc(sizeof(Name_Entry) + len);
+
+ if (entry != NULL) {
+ strcpy(entry->name, name);
+ STAILQ_INSERT_TAIL(&obj->names, entry, link);
+ }
+}
+
+static int
+object_match_name(const Obj_Entry *obj, const char *name)
+{
+ Name_Entry *entry;
+
+ STAILQ_FOREACH(entry, &obj->names, link) {
+ if (strcmp(name, entry->name) == 0)
+ return (1);
+ }
+ return (0);
+}
+
+static Obj_Entry *
+locate_dependency(const Obj_Entry *obj, const char *name)
+{
+ const Objlist_Entry *entry;
+ const Needed_Entry *needed;
+
+ STAILQ_FOREACH(entry, &list_main, link) {
+ if (object_match_name(entry->obj, name))
+ return (entry->obj);
+ }
+
+ for (needed = obj->needed; needed != NULL; needed = needed->next) {
+ if (strcmp(obj->strtab + needed->name, name) == 0 ||
+ (needed->obj != NULL && object_match_name(needed->obj,
+ name))) {
+ /*
+ * If there is DT_NEEDED for the name we are looking
+ * for, we are all set. Note that object might not be
+ * found if dependency was not loaded yet, so the
+ * function can return NULL here. This is expected and
+ * handled properly by the caller.
+ */
+ return (needed->obj);
+ }
+ }
+ _rtld_error("%s: Unexpected inconsistency: dependency %s not found",
+ obj->path, name);
+ rtld_die();
+}
+
+static int
+check_object_provided_version(Obj_Entry *refobj, const Obj_Entry *depobj,
+ const Elf_Vernaux *vna)
+{
+ const Elf_Verdef *vd;
+ const char *vername;
+
+ vername = refobj->strtab + vna->vna_name;
+ vd = depobj->verdef;
+ if (vd == NULL) {
+ _rtld_error("%s: version %s required by %s not defined",
+ depobj->path, vername, refobj->path);
+ return (-1);
+ }
+ for (;;) {
+ if (vd->vd_version != VER_DEF_CURRENT) {
+ _rtld_error(
+ "%s: Unsupported version %d of Elf_Verdef entry",
+ depobj->path, vd->vd_version);
+ return (-1);
+ }
+ if (vna->vna_hash == vd->vd_hash) {
+ const Elf_Verdaux *aux =
+ (const Elf_Verdaux *)((const char *)vd +
+ vd->vd_aux);
+ if (strcmp(vername, depobj->strtab + aux->vda_name) ==
+ 0)
+ return (0);
+ }
+ if (vd->vd_next == 0)
+ break;
+ vd = (const Elf_Verdef *)((const char *)vd + vd->vd_next);
+ }
+ if (vna->vna_flags & VER_FLG_WEAK)
+ return (0);
+ _rtld_error("%s: version %s required by %s not found", depobj->path,
+ vername, refobj->path);
+ return (-1);
+}
+
+static int
+rtld_verify_object_versions(Obj_Entry *obj)
+{
+ const Elf_Verneed *vn;
+ const Elf_Verdef *vd;
+ const Elf_Verdaux *vda;
+ const Elf_Vernaux *vna;
+ const Obj_Entry *depobj;
+ int maxvernum, vernum;
+
+ if (obj->ver_checked)
+ return (0);
+ obj->ver_checked = true;
+
+ maxvernum = 0;
+ /*
+ * Walk over defined and required version records and figure out
+ * max index used by any of them. Do very basic sanity checking
+ * while there.
+ */
+ vn = obj->verneed;
+ while (vn != NULL) {
+ if (vn->vn_version != VER_NEED_CURRENT) {
+ _rtld_error(
+ "%s: Unsupported version %d of Elf_Verneed entry",
+ obj->path, vn->vn_version);
+ return (-1);
+ }
+ vna = (const Elf_Vernaux *)((const char *)vn + vn->vn_aux);
+ for (;;) {
+ vernum = VER_NEED_IDX(vna->vna_other);
+ if (vernum > maxvernum)
+ maxvernum = vernum;
+ if (vna->vna_next == 0)
+ break;
+ vna = (const Elf_Vernaux *)((const char *)vna +
+ vna->vna_next);
+ }
+ if (vn->vn_next == 0)
+ break;
+ vn = (const Elf_Verneed *)((const char *)vn + vn->vn_next);
+ }
+
+ vd = obj->verdef;
+ while (vd != NULL) {
+ if (vd->vd_version != VER_DEF_CURRENT) {
+ _rtld_error(
+ "%s: Unsupported version %d of Elf_Verdef entry",
+ obj->path, vd->vd_version);
+ return (-1);
+ }
+ vernum = VER_DEF_IDX(vd->vd_ndx);
+ if (vernum > maxvernum)
+ maxvernum = vernum;
+ if (vd->vd_next == 0)
+ break;
+ vd = (const Elf_Verdef *)((const char *)vd + vd->vd_next);
+ }
+
+ if (maxvernum == 0)
+ return (0);
+
+ /*
+ * Store version information in array indexable by version index.
+ * Verify that object version requirements are satisfied along the
+ * way.
+ */
+ obj->vernum = maxvernum + 1;
+ obj->vertab = xcalloc(obj->vernum, sizeof(Ver_Entry));
+
+ vd = obj->verdef;
+ while (vd != NULL) {
+ if ((vd->vd_flags & VER_FLG_BASE) == 0) {
+ vernum = VER_DEF_IDX(vd->vd_ndx);
+ assert(vernum <= maxvernum);
+ vda = (const Elf_Verdaux *)((const char *)vd +
+ vd->vd_aux);
+ obj->vertab[vernum].hash = vd->vd_hash;
+ obj->vertab[vernum].name = obj->strtab + vda->vda_name;
+ obj->vertab[vernum].file = NULL;
+ obj->vertab[vernum].flags = 0;
+ }
+ if (vd->vd_next == 0)
+ break;
+ vd = (const Elf_Verdef *)((const char *)vd + vd->vd_next);
+ }
+
+ vn = obj->verneed;
+ while (vn != NULL) {
+ depobj = locate_dependency(obj, obj->strtab + vn->vn_file);
+ if (depobj == NULL)
+ return (-1);
+ vna = (const Elf_Vernaux *)((const char *)vn + vn->vn_aux);
+ for (;;) {
+ if (check_object_provided_version(obj, depobj, vna))
+ return (-1);
+ vernum = VER_NEED_IDX(vna->vna_other);
+ assert(vernum <= maxvernum);
+ obj->vertab[vernum].hash = vna->vna_hash;
+ obj->vertab[vernum].name = obj->strtab + vna->vna_name;
+ obj->vertab[vernum].file = obj->strtab + vn->vn_file;
+ obj->vertab[vernum].flags = (vna->vna_other &
+ VER_NEED_HIDDEN) != 0 ? VER_INFO_HIDDEN : 0;
+ if (vna->vna_next == 0)
+ break;
+ vna = (const Elf_Vernaux *)((const char *)vna +
+ vna->vna_next);
+ }
+ if (vn->vn_next == 0)
+ break;
+ vn = (const Elf_Verneed *)((const char *)vn + vn->vn_next);
+ }
+ return (0);
+}
+
+static int
+rtld_verify_versions(const Objlist *objlist)
+{
+ Objlist_Entry *entry;
+ int rc;
+
+ rc = 0;
+ STAILQ_FOREACH(entry, objlist, link) {
+ /*
+ * Skip dummy objects or objects that have their version
+ * requirements already checked.
+ */
+ if (entry->obj->strtab == NULL || entry->obj->vertab != NULL)
+ continue;
+ if (rtld_verify_object_versions(entry->obj) == -1) {
+ rc = -1;
+ if (ld_tracing == NULL)
+ break;
+ }
+ }
+ if (rc == 0 || ld_tracing != NULL)
+ rc = rtld_verify_object_versions(&obj_rtld);
+ return (rc);
+}
+
+const Ver_Entry *
+fetch_ventry(const Obj_Entry *obj, unsigned long symnum)
+{
+ Elf_Versym vernum;
+
+ if (obj->vertab) {
+ vernum = VER_NDX(obj->versyms[symnum]);
+ if (vernum >= obj->vernum) {
+ _rtld_error("%s: symbol %s has wrong verneed value %d",
+ obj->path, obj->strtab + symnum, vernum);
+ } else if (obj->vertab[vernum].hash != 0) {
+ return (&obj->vertab[vernum]);
+ }
+ }
+ return (NULL);
+}
+
+int
+_rtld_get_stack_prot(void)
+{
+ return (stack_prot);
+}
+
+int
+_rtld_is_dlopened(void *arg)
+{
+ Obj_Entry *obj;
+ RtldLockState lockstate;
+ int res;
+
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ obj = dlcheck(arg);
+ if (obj == NULL)
+ obj = obj_from_addr(arg);
+ if (obj == NULL) {
+ _rtld_error("No shared object contains address");
+ lock_release(rtld_bind_lock, &lockstate);
+ return (-1);
+ }
+ res = obj->dlopened ? 1 : 0;
+ lock_release(rtld_bind_lock, &lockstate);
+ return (res);
+}
+
+static int
+obj_remap_relro(Obj_Entry *obj, int prot)
+{
+ const Elf_Phdr *ph;
+ caddr_t relro_page;
+ size_t relro_size;
+
+ for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr +
+ obj->phsize; ph++) {
+ if (ph->p_type != PT_GNU_RELRO)
+ continue;
+ relro_page = obj->relocbase + rtld_trunc_page(ph->p_vaddr);
+ relro_size = rtld_round_page(ph->p_vaddr + ph->p_memsz) -
+ rtld_trunc_page(ph->p_vaddr);
+ if (mprotect(relro_page, relro_size, prot) == -1) {
+ _rtld_error(
+ "%s: Cannot set relro protection to %#x: %s",
+ obj->path, prot, rtld_strerror(errno));
+ return (-1);
+ }
+ break;
+ }
+ return (0);
+}
+
+static int
+obj_disable_relro(Obj_Entry *obj)
+{
+ return (obj_remap_relro(obj, PROT_READ | PROT_WRITE));
+}
+
+static int
+obj_enforce_relro(Obj_Entry *obj)
+{
+ return (obj_remap_relro(obj, PROT_READ));
+}
+
+static void
+map_stacks_exec(RtldLockState *lockstate)
+{
+ void (*thr_map_stacks_exec)(void);
+
+ if ((max_stack_flags & PF_X) == 0 || (stack_prot & PROT_EXEC) != 0)
+ return;
+ thr_map_stacks_exec = (void (*)(void))(
+ uintptr_t)get_program_var_addr("__pthread_map_stacks_exec",
+ lockstate);
+ if (thr_map_stacks_exec != NULL) {
+ stack_prot |= PROT_EXEC;
+ thr_map_stacks_exec();
+ }
+}
+
+static void
+distribute_static_tls(Objlist *list)
+{
+ struct tcb_list_entry *tcbelm;
+ Objlist_Entry *objelm;
+ struct tcb *tcb;
+ Obj_Entry *obj;
+ char *tlsbase;
+
+ STAILQ_FOREACH(objelm, list, link) {
+ obj = objelm->obj;
+ if (obj->marker || !obj->tls_static || obj->static_tls_copied)
+ continue;
+ TAILQ_FOREACH(tcbelm, &tcb_list, next) {
+ tcb = tcb_from_tcb_list_entry(tcbelm);
+#ifdef TLS_VARIANT_I
+ tlsbase = (char *)tcb + obj->tlsoffset;
+#else
+ tlsbase = (char *)tcb - obj->tlsoffset;
+#endif
+ memcpy(tlsbase, obj->tlsinit, obj->tlsinitsize);
+ memset(tlsbase + obj->tlsinitsize, 0,
+ obj->tlssize - obj->tlsinitsize);
+ }
+ obj->static_tls_copied = true;
+ }
+}
+
+void
+symlook_init(SymLook *dst, const char *name)
+{
+ bzero(dst, sizeof(*dst));
+ dst->name = name;
+ dst->hash = elf_hash(name);
+ dst->hash_gnu = gnu_hash(name);
+}
+
+static void
+symlook_init_from_req(SymLook *dst, const SymLook *src)
+{
+ dst->name = src->name;
+ dst->hash = src->hash;
+ dst->hash_gnu = src->hash_gnu;
+ dst->ventry = src->ventry;
+ dst->flags = src->flags;
+ dst->defobj_out = NULL;
+ dst->sym_out = NULL;
+ dst->lockstate = src->lockstate;
+}
+
+static int
+open_binary_fd(const char *argv0, bool search_in_path, const char **binpath_res)
+{
+ char *binpath, *pathenv, *pe, *res1;
+ const char *res;
+ int fd;
+
+ binpath = NULL;
+ res = NULL;
+ if (search_in_path && strchr(argv0, '/') == NULL) {
+ binpath = xmalloc(PATH_MAX);
+ pathenv = getenv("PATH");
+ if (pathenv == NULL) {
+ _rtld_error("-p and no PATH environment variable");
+ rtld_die();
+ }
+ pathenv = strdup(pathenv);
+ if (pathenv == NULL) {
+ _rtld_error("Cannot allocate memory");
+ rtld_die();
+ }
+ fd = -1;
+ errno = ENOENT;
+ while ((pe = strsep(&pathenv, ":")) != NULL) {
+ if (strlcpy(binpath, pe, PATH_MAX) >= PATH_MAX)
+ continue;
+ if (binpath[0] != '\0' &&
+ strlcat(binpath, "/", PATH_MAX) >= PATH_MAX)
+ continue;
+ if (strlcat(binpath, argv0, PATH_MAX) >= PATH_MAX)
+ continue;
+ fd = open(binpath, O_RDONLY | O_CLOEXEC | O_VERIFY);
+ if (fd != -1 || errno != ENOENT) {
+ res = binpath;
+ break;
+ }
+ }
+ free(pathenv);
+ } else {
+ fd = open(argv0, O_RDONLY | O_CLOEXEC | O_VERIFY);
+ res = argv0;
+ }
+
+ if (fd == -1) {
+ _rtld_error("Cannot open %s: %s", argv0, rtld_strerror(errno));
+ rtld_die();
+ }
+ if (res != NULL && res[0] != '/') {
+ res1 = xmalloc(PATH_MAX);
+ if (realpath(res, res1) != NULL) {
+ if (res != argv0)
+ free(__DECONST(char *, res));
+ res = res1;
+ } else {
+ free(res1);
+ }
+ }
+ *binpath_res = res;
+ return (fd);
+}
+
+/*
+ * Parse a set of command-line arguments.
+ */
+static int
+parse_args(char *argv[], int argc, bool *use_pathp, int *fdp,
+ const char **argv0, bool *dir_ignore)
+{
+ const char *arg;
+ char machine[64];
+ size_t sz;
+ int arglen, fd, i, j, mib[2];
+ char opt;
+ bool seen_b, seen_f;
+
+ dbg("Parsing command-line arguments");
+ *use_pathp = false;
+ *fdp = -1;
+ *dir_ignore = false;
+ seen_b = seen_f = false;
+
+ for (i = 1; i < argc; i++) {
+ arg = argv[i];
+ dbg("argv[%d]: '%s'", i, arg);
+
+ /*
+ * rtld arguments end with an explicit "--" or with the first
+ * non-prefixed argument.
+ */
+ if (strcmp(arg, "--") == 0) {
+ i++;
+ break;
+ }
+ if (arg[0] != '-')
+ break;
+
+ /*
+ * All other arguments are single-character options that can
+ * be combined, so we need to search through `arg` for them.
+ */
+ arglen = strlen(arg);
+ for (j = 1; j < arglen; j++) {
+ opt = arg[j];
+ if (opt == 'h') {
+ print_usage(argv[0]);
+ _exit(0);
+ } else if (opt == 'b') {
+ if (seen_f) {
+ _rtld_error("Both -b and -f specified");
+ rtld_die();
+ }
+ if (j != arglen - 1) {
+ _rtld_error("Invalid options: %s", arg);
+ rtld_die();
+ }
+ i++;
+ *argv0 = argv[i];
+ seen_b = true;
+ break;
+ } else if (opt == 'd') {
+ *dir_ignore = true;
+ } else if (opt == 'f') {
+ if (seen_b) {
+ _rtld_error("Both -b and -f specified");
+ rtld_die();
+ }
+
+ /*
+ * -f XX can be used to specify a
+ * descriptor for the binary named at
+ * the command line (i.e., the later
+ * argument will specify the process
+ * name but the descriptor is what
+ * will actually be executed).
+ *
+ * -f must be the last option in the
+ * group, e.g., -abcf <fd>.
+ */
+ if (j != arglen - 1) {
+ _rtld_error("Invalid options: %s", arg);
+ rtld_die();
+ }
+ i++;
+ fd = parse_integer(argv[i]);
+ if (fd == -1) {
+ _rtld_error(
+ "Invalid file descriptor: '%s'",
+ argv[i]);
+ rtld_die();
+ }
+ *fdp = fd;
+ seen_f = true;
+ break;
+ } else if (opt == 'o') {
+ struct ld_env_var_desc *l;
+ char *n, *v;
+ u_int ll;
+
+ if (j != arglen - 1) {
+ _rtld_error("Invalid options: %s", arg);
+ rtld_die();
+ }
+ i++;
+ n = argv[i];
+ v = strchr(n, '=');
+ if (v == NULL) {
+ _rtld_error("No '=' in -o parameter");
+ rtld_die();
+ }
+ for (ll = 0; ll < nitems(ld_env_vars); ll++) {
+ l = &ld_env_vars[ll];
+ if (v - n == (ptrdiff_t)strlen(l->n) &&
+ strncmp(n, l->n, v - n) == 0) {
+ l->val = v + 1;
+ break;
+ }
+ }
+ if (ll == nitems(ld_env_vars)) {
+ _rtld_error("Unknown LD_ option %s", n);
+ rtld_die();
+ }
+ } else if (opt == 'p') {
+ *use_pathp = true;
+ } else if (opt == 'u') {
+ u_int ll;
+
+ for (ll = 0; ll < nitems(ld_env_vars); ll++)
+ ld_env_vars[ll].val = NULL;
+ } else if (opt == 'v') {
+ machine[0] = '\0';
+ mib[0] = CTL_HW;
+ mib[1] = HW_MACHINE;
+ sz = sizeof(machine);
+ sysctl(mib, nitems(mib), machine, &sz, NULL, 0);
+ ld_elf_hints_path = ld_get_env_var(
+ LD_ELF_HINTS_PATH);
+ set_ld_elf_hints_path();
+ rtld_printf(
+ "FreeBSD ld-elf.so.1 %s\n"
+ "FreeBSD_version %d\n"
+ "Default lib path %s\n"
+ "Hints lib path %s\n"
+ "Env prefix %s\n"
+ "Default hint file %s\n"
+ "Hint file %s\n"
+ "libmap file %s\n"
+ "Optional static TLS size %zd bytes\n",
+ machine, __FreeBSD_version,
+ ld_standard_library_path, gethints(false),
+ ld_env_prefix, ld_elf_hints_default,
+ ld_elf_hints_path, ld_path_libmap_conf,
+ ld_static_tls_extra);
+ _exit(0);
+ } else {
+ _rtld_error("Invalid argument: '%s'", arg);
+ print_usage(argv[0]);
+ rtld_die();
+ }
+ }
+ }
+
+ if (!seen_b)
+ *argv0 = argv[i];
+ return (i);
+}
+
+/*
+ * Parse a file descriptor number without pulling in more of libc (e.g. atoi).
+ */
+static int
+parse_integer(const char *str)
+{
+ static const int RADIX = 10; /* XXXJA: possibly support hex? */
+ const char *orig;
+ int n;
+ char c;
+
+ orig = str;
+ n = 0;
+ for (c = *str; c != '\0'; c = *++str) {
+ if (c < '0' || c > '9')
+ return (-1);
+
+ n *= RADIX;
+ n += c - '0';
+ }
+
+ /* Make sure we actually parsed something. */
+ if (str == orig)
+ return (-1);
+ return (n);
+}
+
+static void
+print_usage(const char *argv0)
+{
+ rtld_printf(
+ "Usage: %s [-h] [-b <exe>] [-d] [-f <FD>] [-p] [--] <binary> [<args>]\n"
+ "\n"
+ "Options:\n"
+ " -h Display this help message\n"
+ " -b <exe> Execute <exe> instead of <binary>, arg0 is <binary>\n"
+ " -d Ignore lack of exec permissions for the binary\n"
+ " -f <FD> Execute <FD> instead of searching for <binary>\n"
+ " -o <OPT>=<VAL> Set LD_<OPT> to <VAL>, without polluting env\n"
+ " -p Search in PATH for named binary\n"
+ " -u Ignore LD_ environment variables\n"
+ " -v Display identification information\n"
+ " -- End of RTLD options\n"
+ " <binary> Name of process to execute\n"
+ " <args> Arguments to the executed process\n",
+ argv0);
+}
+
+#define AUXFMT(at, xfmt) [at] = { .name = #at, .fmt = xfmt }
+static const struct auxfmt {
+ const char *name;
+ const char *fmt;
+} auxfmts[] = {
+ AUXFMT(AT_NULL, NULL),
+ AUXFMT(AT_IGNORE, NULL),
+ AUXFMT(AT_EXECFD, "%ld"),
+ AUXFMT(AT_PHDR, "%p"),
+ AUXFMT(AT_PHENT, "%lu"),
+ AUXFMT(AT_PHNUM, "%lu"),
+ AUXFMT(AT_PAGESZ, "%lu"),
+ AUXFMT(AT_BASE, "%#lx"),
+ AUXFMT(AT_FLAGS, "%#lx"),
+ AUXFMT(AT_ENTRY, "%p"),
+ AUXFMT(AT_NOTELF, NULL),
+ AUXFMT(AT_UID, "%ld"),
+ AUXFMT(AT_EUID, "%ld"),
+ AUXFMT(AT_GID, "%ld"),
+ AUXFMT(AT_EGID, "%ld"),
+ AUXFMT(AT_EXECPATH, "%s"),
+ AUXFMT(AT_CANARY, "%p"),
+ AUXFMT(AT_CANARYLEN, "%lu"),
+ AUXFMT(AT_OSRELDATE, "%lu"),
+ AUXFMT(AT_NCPUS, "%lu"),
+ AUXFMT(AT_PAGESIZES, "%p"),
+ AUXFMT(AT_PAGESIZESLEN, "%lu"),
+ AUXFMT(AT_TIMEKEEP, "%p"),
+ AUXFMT(AT_STACKPROT, "%#lx"),
+ AUXFMT(AT_EHDRFLAGS, "%#lx"),
+ AUXFMT(AT_HWCAP, "%#lx"),
+ AUXFMT(AT_HWCAP2, "%#lx"),
+ AUXFMT(AT_BSDFLAGS, "%#lx"),
+ AUXFMT(AT_ARGC, "%lu"),
+ AUXFMT(AT_ARGV, "%p"),
+ AUXFMT(AT_ENVC, "%p"),
+ AUXFMT(AT_ENVV, "%p"),
+ AUXFMT(AT_PS_STRINGS, "%p"),
+ AUXFMT(AT_FXRNG, "%p"),
+ AUXFMT(AT_KPRELOAD, "%p"),
+ AUXFMT(AT_USRSTACKBASE, "%#lx"),
+ AUXFMT(AT_USRSTACKLIM, "%#lx"),
+ /* AT_CHERI_STATS */
+ AUXFMT(AT_HWCAP3, "%#lx"),
+ AUXFMT(AT_HWCAP4, "%#lx"),
+
+};
+
+static bool
+is_ptr_fmt(const char *fmt)
+{
+ char last;
+
+ last = fmt[strlen(fmt) - 1];
+ return (last == 'p' || last == 's');
+}
+
+static void
+dump_auxv(Elf_Auxinfo **aux_info)
+{
+ Elf_Auxinfo *auxp;
+ const struct auxfmt *fmt;
+ int i;
+
+ for (i = 0; i < AT_COUNT; i++) {
+ auxp = aux_info[i];
+ if (auxp == NULL)
+ continue;
+ fmt = &auxfmts[i];
+ if (fmt->fmt == NULL)
+ continue;
+ rtld_fdprintf(STDOUT_FILENO, "%s:\t", fmt->name);
+ if (is_ptr_fmt(fmt->fmt)) {
+ rtld_fdprintfx(STDOUT_FILENO, fmt->fmt,
+ auxp->a_un.a_ptr);
+ } else {
+ rtld_fdprintfx(STDOUT_FILENO, fmt->fmt,
+ auxp->a_un.a_val);
+ }
+ rtld_fdprintf(STDOUT_FILENO, "\n");
+ }
+}
+
+const char *
+rtld_get_var(const char *name)
+{
+ const struct ld_env_var_desc *lvd;
+ u_int i;
+
+ for (i = 0; i < nitems(ld_env_vars); i++) {
+ lvd = &ld_env_vars[i];
+ if (strcmp(lvd->n, name) == 0)
+ return (lvd->val);
+ }
+ return (NULL);
+}
+
+int
+rtld_set_var(const char *name, const char *val)
+{
+ struct ld_env_var_desc *lvd;
+ u_int i;
+
+ for (i = 0; i < nitems(ld_env_vars); i++) {
+ lvd = &ld_env_vars[i];
+ if (strcmp(lvd->n, name) != 0)
+ continue;
+ if (!lvd->can_update || (lvd->unsecure && !trust))
+ return (EPERM);
+ if (lvd->owned)
+ free(__DECONST(char *, lvd->val));
+ if (val != NULL)
+ lvd->val = xstrdup(val);
+ else
+ lvd->val = NULL;
+ lvd->owned = true;
+ if (lvd->debug)
+ debug = lvd->val != NULL && *lvd->val != '\0';
+ return (0);
+ }
+ return (ENOENT);
+}
+
+/*
+ * Overrides for libc_pic-provided functions.
+ */
+
+int
+__getosreldate(void)
+{
+ size_t len;
+ int oid[2];
+ int error, osrel;
+
+ if (osreldate != 0)
+ return (osreldate);
+
+ oid[0] = CTL_KERN;
+ oid[1] = KERN_OSRELDATE;
+ osrel = 0;
+ len = sizeof(osrel);
+ error = sysctl(oid, 2, &osrel, &len, NULL, 0);
+ if (error == 0 && osrel > 0 && len == sizeof(osrel))
+ osreldate = osrel;
+ return (osreldate);
+}
+const char *
+rtld_strerror(int errnum)
+{
+ if (errnum < 0 || errnum >= sys_nerr)
+ return ("Unknown error");
+ return (sys_errlist[errnum]);
+}
+
+char *
+getenv(const char *name)
+{
+ return (__DECONST(char *, rtld_get_env_val(environ, name,
+ strlen(name))));
+}
+
+/* malloc */
+void *
+malloc(size_t nbytes)
+{
+ return (__crt_malloc(nbytes));
+}
+
+void *
+calloc(size_t num, size_t size)
+{
+ return (__crt_calloc(num, size));
+}
+
+void
+free(void *cp)
+{
+ __crt_free(cp);
+}
+
+void *
+realloc(void *cp, size_t nbytes)
+{
+ return (__crt_realloc(cp, nbytes));
+}
+
+extern int _rtld_version__FreeBSD_version __exported;
+int _rtld_version__FreeBSD_version = __FreeBSD_version;
+
+extern char _rtld_version_laddr_offset __exported;
+char _rtld_version_laddr_offset;
+
+extern char _rtld_version_dlpi_tls_data __exported;
+char _rtld_version_dlpi_tls_data;
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
new file mode 100644
index 000000000000..d4829b17cebb
--- /dev/null
+++ b/libexec/rtld-elf/rtld.h
@@ -0,0 +1,443 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RTLD_H /* { */
+#define RTLD_H 1
+
+#include <machine/elf.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <elf-hints.h>
+#include <link.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <setjmp.h>
+#include <stddef.h>
+
+#include "rtld_lock.h"
+#include "rtld_machdep.h"
+
+#define NEW(type) ((type *) xmalloc(sizeof(type)))
+#define CNEW(type) ((type *) xcalloc(1, sizeof(type)))
+
+extern size_t tls_last_offset;
+extern size_t tls_last_size;
+extern size_t tls_static_space;
+extern Elf_Addr tls_dtv_generation;
+extern int tls_max_index;
+extern size_t ld_static_tls_extra;
+
+extern int npagesizes;
+extern size_t *pagesizes;
+extern size_t page_size;
+
+extern int main_argc;
+extern char **main_argv;
+extern char **environ;
+
+struct stat;
+struct Struct_Obj_Entry;
+
+/* Lists of shared objects */
+typedef struct Struct_Objlist_Entry {
+ STAILQ_ENTRY(Struct_Objlist_Entry) link;
+ struct Struct_Obj_Entry *obj;
+} Objlist_Entry;
+
+typedef STAILQ_HEAD(Struct_Objlist, Struct_Objlist_Entry) Objlist;
+
+/* Types of init and fini functions */
+typedef void (*InitFunc)(void);
+typedef void (*InitArrFunc)(int, char **, char **);
+
+/* Lists of shared object dependencies */
+typedef struct Struct_Needed_Entry {
+ struct Struct_Needed_Entry *next;
+ struct Struct_Obj_Entry *obj;
+ unsigned long name; /* Offset of name in string table */
+} Needed_Entry;
+
+typedef struct Struct_Name_Entry {
+ STAILQ_ENTRY(Struct_Name_Entry) link;
+ char name[1];
+} Name_Entry;
+
+/* Lock object */
+typedef struct Struct_LockInfo {
+ void *context; /* Client context for creating locks */
+ void *thelock; /* The one big lock */
+ /* Debugging aids. */
+ volatile int rcount; /* Number of readers holding lock */
+ volatile int wcount; /* Number of writers holding lock */
+ /* Methods */
+ void *(*lock_create)(void *context);
+ void (*rlock_acquire)(void *lock);
+ void (*wlock_acquire)(void *lock);
+ void (*rlock_release)(void *lock);
+ void (*wlock_release)(void *lock);
+ void (*lock_destroy)(void *lock);
+ void (*context_destroy)(void *context);
+} LockInfo;
+
+typedef struct Struct_Ver_Entry {
+ Elf_Word hash;
+ unsigned int flags;
+ const char *name;
+ const char *file;
+} Ver_Entry;
+
+typedef struct Struct_Sym_Match_Result {
+ const Elf_Sym *sym_out;
+ const Elf_Sym *vsymp;
+ int vcount;
+} Sym_Match_Result;
+
+#define VER_INFO_HIDDEN 0x01
+
+/*
+ * Shared object descriptor.
+ *
+ * Items marked with "(%)" are dynamically allocated, and must be freed
+ * when the structure is destroyed.
+ *
+ * CAUTION: It appears that the JDK port peeks into these structures.
+ * It looks at "next" and "mapbase" at least. Don't add new members
+ * near the front, until this can be straightened out.
+ */
+typedef struct Struct_Obj_Entry {
+ /*
+ * These two items have to be set right for compatibility with the
+ * original ElfKit crt1.o.
+ */
+ Elf_Size magic; /* Magic number (sanity check) */
+ Elf_Size version; /* Version number of struct format */
+
+ TAILQ_ENTRY(Struct_Obj_Entry) next;
+ char *path; /* Pathname of underlying file (%) */
+ char *origin_path; /* Directory path of origin file */
+ int refcount; /* DAG references */
+ int holdcount; /* Count of transient references */
+ int dl_refcount; /* Number of times loaded by dlopen */
+
+ /* These items are computed by map_object() or by digest_phdr(). */
+ caddr_t mapbase; /* Base address of mapped region */
+ size_t mapsize; /* Size of mapped region in bytes */
+ Elf_Addr vaddrbase; /* Base address in shared object file */
+ caddr_t relocbase; /* Relocation constant = mapbase - vaddrbase */
+ const Elf_Dyn *dynamic; /* Dynamic section */
+ caddr_t entry; /* Entry point */
+ const Elf_Phdr *phdr; /* Program header if it is mapped, else NULL */
+ size_t phsize; /* Size of program header in bytes */
+ const char *interp; /* Pathname of the interpreter, if any */
+ Elf_Word stack_flags;
+
+ /* TLS information */
+ int tlsindex; /* Index in DTV for this module */
+ void *tlsinit; /* Base address of TLS init block */
+ size_t tlsinitsize; /* Size of TLS init block for this module */
+ size_t tlssize; /* Size of TLS block for this module */
+ size_t tlsoffset; /* Offset of static TLS block for this module */
+ size_t tlsalign; /* Alignment of static TLS block */
+ size_t tlspoffset; /* p_offset of the static TLS block */
+
+ /* Items from the dynamic section. */
+ Elf_Addr *pltgot; /* PLT or GOT, depending on architecture */
+ const Elf_Rel *rel; /* Relocation entries */
+ unsigned long relsize; /* Size in bytes of relocation info */
+ const Elf_Rela *rela; /* Relocation entries with addend */
+ unsigned long relasize; /* Size in bytes of addend relocation info */
+ const Elf_Relr *relr; /* RELR relocation entries */
+ unsigned long relrsize; /* Size in bytes of RELR relocations */
+ const Elf_Rel *pltrel; /* PLT relocation entries */
+ unsigned long pltrelsize; /* Size in bytes of PLT relocation info */
+ const Elf_Rela *pltrela; /* PLT relocation entries with addend */
+ unsigned long pltrelasize; /* Size in bytes of PLT addend reloc info */
+ const Elf_Sym *symtab; /* Symbol table */
+ const char *strtab; /* String table */
+ unsigned long strsize; /* Size in bytes of string table */
+
+ const Elf_Verneed *verneed; /* Required versions. */
+ Elf_Word verneednum; /* Number of entries in verneed table */
+ const Elf_Verdef *verdef; /* Provided versions. */
+ Elf_Word verdefnum; /* Number of entries in verdef table */
+ const Elf_Versym *versyms; /* Symbol versions table */
+
+ const Elf_Hashelt *buckets; /* Hash table buckets array */
+ unsigned long nbuckets; /* Number of buckets */
+ const Elf_Hashelt *chains; /* Hash table chain array */
+ unsigned long nchains; /* Number of entries in chain array */
+
+ Elf32_Word nbuckets_gnu; /* Number of GNU hash buckets*/
+ Elf32_Word symndx_gnu; /* 1st accessible symbol on dynsym table */
+ Elf32_Word maskwords_bm_gnu; /* Bloom filter words - 1 (bitmask) */
+ Elf32_Word shift2_gnu; /* Bloom filter shift count */
+ Elf32_Word dynsymcount; /* Total entries in dynsym table */
+ const Elf_Addr *bloom_gnu; /* Bloom filter used by GNU hash func */
+ const Elf_Hashelt *buckets_gnu; /* GNU hash table bucket array */
+ const Elf_Hashelt *chain_zero_gnu; /* GNU hash table value array (Zeroed) */
+
+ const char *rpath; /* Search path specified in object */
+ const char *runpath; /* Search path with different priority */
+ Needed_Entry *needed; /* Shared objects needed by this one (%) */
+ Needed_Entry *needed_filtees;
+ Needed_Entry *needed_aux_filtees;
+
+ STAILQ_HEAD(, Struct_Name_Entry) names; /* List of names for this object we
+ know about. */
+ Ver_Entry *vertab; /* Versions required /defined by this object */
+ int vernum; /* Number of entries in vertab */
+
+ Elf_Addr init; /* Initialization function to call */
+ Elf_Addr fini; /* Termination function to call */
+ Elf_Addr preinit_array; /* Pre-initialization array of functions */
+ Elf_Addr init_array; /* Initialization array of functions */
+ Elf_Addr fini_array; /* Termination array of functions */
+ int preinit_array_num; /* Number of entries in preinit_array */
+ int init_array_num; /* Number of entries in init_array */
+ int fini_array_num; /* Number of entries in fini_array */
+
+ int32_t osrel; /* OSREL note value */
+ uint32_t fctl0; /* FEATURE_CONTROL note desc[0] value */
+
+ bool mainprog : 1; /* True if this is the main program */
+ bool rtld : 1; /* True if this is the dynamic linker */
+ bool relocated : 1; /* True if processed by relocate_objects() */
+ bool ver_checked : 1; /* True if processed by rtld_verify_object_versions */
+ bool textrel : 1; /* True if there are relocations to text seg */
+ bool symbolic : 1; /* True if generated with "-Bsymbolic" */
+ bool deepbind : 1; /* True if loaded with RTLD_DEEPBIND" */
+ bool bind_now : 1; /* True if all relocations should be made first */
+ bool traced : 1; /* Already printed in ldd trace output */
+ bool jmpslots_done : 1; /* Already have relocated the jump slots */
+ bool init_done : 1; /* Already have added object to init list */
+ bool tls_static : 1; /* Already allocated offset for static TLS */
+ bool tls_dynamic : 1; /* A non-static DTV entry has been allocated */
+ bool phdr_alloc : 1; /* Phdr is allocated and needs to be freed. */
+ bool z_origin : 1; /* Process rpath and soname tokens */
+ bool z_nodelete : 1; /* Do not unload the object and dependencies */
+ bool z_noopen : 1; /* Do not load on dlopen */
+ bool z_loadfltr : 1; /* Immediately load filtees */
+ bool z_interpose : 1; /* Interpose all objects but main */
+ bool z_nodeflib : 1; /* Don't search default library path */
+ bool z_global : 1; /* Make the object global */
+ bool z_pie : 1; /* Object proclaimed itself PIE executable */
+ bool z_initfirst : 1; /* Proceed initializers before other objects */
+ bool static_tls : 1; /* Needs static TLS allocation */
+ bool static_tls_copied : 1; /* Needs static TLS copying */
+ bool ref_nodel : 1; /* Refcount increased to prevent dlclose */
+ bool init_scanned: 1; /* Object is already on init list. */
+ bool on_fini_list: 1; /* Object is already on fini list. */
+ bool dag_inited : 1; /* Object has its DAG initialized. */
+ bool filtees_loaded : 1; /* Filtees loaded */
+ bool filtees_loading : 1; /* In process of filtees loading */
+ bool irelative : 1; /* Object has R_MACHDEP_IRELATIVE relocs */
+ bool irelative_nonplt : 1; /* Object has R_MACHDEP_IRELATIVE non-plt relocs */
+ bool gnu_ifunc : 1; /* Object has references to STT_GNU_IFUNC */
+ bool non_plt_gnu_ifunc : 1; /* Object has non-plt IFUNC references */
+ bool ifuncs_resolved : 1; /* Object ifuncs were already resolved */
+ bool crt_no_init : 1; /* Object' crt does not call _init/_fini */
+ bool valid_hash_sysv : 1; /* A valid System V hash hash tag is available */
+ bool valid_hash_gnu : 1; /* A valid GNU hash tag is available */
+ bool dlopened : 1; /* dlopen()-ed (vs. load statically) */
+ bool marker : 1; /* marker on the global obj list */
+ bool unholdfree : 1; /* unmap upon last unhold */
+ bool doomed : 1; /* Object cannot be referenced */
+
+ MD_OBJ_ENTRY
+
+ struct link_map linkmap; /* For GDB and dlinfo() */
+ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */
+ Objlist dagmembers; /* DAG has these members (%) */
+ dev_t dev; /* Object's filesystem's device */
+ ino_t ino; /* Object's inode number */
+ void *priv; /* Platform-dependent */
+} Obj_Entry;
+
+#define RTLD_MAGIC 0xd550b87a
+#define RTLD_VERSION 1
+
+TAILQ_HEAD(obj_entry_q, Struct_Obj_Entry);
+
+#define RTLD_STATIC_TLS_EXTRA 128
+
+/* Flags to be passed into symlook_ family of functions. */
+#define SYMLOOK_IN_PLT 0x01 /* Lookup for PLT symbol */
+#define SYMLOOK_DLSYM 0x02 /* Return newest versioned symbol. Used by
+ dlsym. */
+#define SYMLOOK_EARLY 0x04 /* Symlook is done during initialization. */
+#define SYMLOOK_IFUNC 0x08 /* Allow IFUNC processing in
+ reloc_non_plt(). */
+
+/* Flags for load_object(). */
+#define RTLD_LO_NOLOAD 0x01 /* dlopen() specified RTLD_NOLOAD. */
+#define RTLD_LO_DLOPEN 0x02 /* Load_object() called from dlopen(). */
+#define RTLD_LO_TRACE 0x04 /* Only tracing. */
+#define RTLD_LO_NODELETE 0x08 /* Loaded object cannot be closed. */
+#define RTLD_LO_FILTEES 0x10 /* Loading filtee. */
+#define RTLD_LO_EARLY 0x20 /* Do not call ctors, postpone it to the
+ initialization during the image start. */
+#define RTLD_LO_IGNSTLS 0x40 /* Do not allocate static TLS */
+#define RTLD_LO_DEEPBIND 0x80 /* Force symbolic for this object */
+
+/*
+ * Symbol cache entry used during relocation to avoid multiple lookups
+ * of the same symbol.
+ */
+typedef struct Struct_SymCache {
+ const Elf_Sym *sym; /* Symbol table entry */
+ const Obj_Entry *obj; /* Shared object which defines it */
+} SymCache;
+
+/*
+ * This structure provides a reentrant way to keep a list of objects and
+ * check which ones have already been processed in some way.
+ */
+typedef struct Struct_DoneList {
+ const Obj_Entry **objs; /* Array of object pointers */
+ unsigned int num_alloc; /* Allocated size of the array */
+ unsigned int num_used; /* Number of array slots used */
+} DoneList;
+
+struct Struct_RtldLockState {
+ int lockstate;
+ sigjmp_buf env;
+};
+
+struct fill_search_info_args {
+ int request;
+ unsigned int flags;
+ struct dl_serinfo *serinfo;
+ struct dl_serpath *serpath;
+ char *strspace;
+};
+
+/*
+ * The pack of arguments and results for the symbol lookup functions.
+ */
+typedef struct Struct_SymLook {
+ const char *name;
+ unsigned long hash;
+ uint32_t hash_gnu;
+ const Ver_Entry *ventry;
+ int flags;
+ const Obj_Entry *defobj_out;
+ const Elf_Sym *sym_out;
+ struct Struct_RtldLockState *lockstate;
+} SymLook;
+
+enum {
+ LD_BIND_NOW = 0,
+ LD_PRELOAD,
+ LD_LIBMAP,
+ LD_LIBRARY_PATH,
+ LD_LIBRARY_PATH_FDS,
+ LD_LIBMAP_DISABLE,
+ LD_BIND_NOT,
+ LD_DEBUG,
+ LD_ELF_HINTS_PATH,
+ LD_LOADFLTR,
+ LD_LIBRARY_PATH_RPATH,
+ LD_PRELOAD_FDS,
+ LD_DYNAMIC_WEAK,
+ LD_TRACE_LOADED_OBJECTS,
+ LD_UTRACE,
+ LD_DUMP_REL_PRE,
+ LD_DUMP_REL_POST,
+ LD_TRACE_LOADED_OBJECTS_PROGNAME,
+ LD_TRACE_LOADED_OBJECTS_FMT1,
+ LD_TRACE_LOADED_OBJECTS_FMT2,
+ LD_TRACE_LOADED_OBJECTS_ALL,
+ LD_SHOW_AUXV,
+ LD_STATIC_TLS_EXTRA,
+ LD_NO_DL_ITERATE_PHDR_AFTER_FORK,
+};
+
+void _rtld_error(const char *, ...) __printflike(1, 2) __exported;
+void rtld_die(void) __dead2;
+const char *rtld_strerror(int);
+Obj_Entry *map_object(int, const char *, const struct stat *, bool);
+void *xcalloc(size_t, size_t);
+void *xmalloc(size_t);
+char *xstrdup(const char *);
+void *xmalloc_aligned(size_t size, size_t align, size_t offset);
+extern Elf_Addr _GLOBAL_OFFSET_TABLE_[];
+extern Elf_Sym sym_zero; /* For resolving undefined weak refs. */
+extern bool ld_bind_not;
+extern bool ld_fast_sigblock;
+
+void dump_relocations(Obj_Entry *);
+void dump_obj_relocations(Obj_Entry *);
+void dump_Elf_Rel(Obj_Entry *, const Elf_Rel *, u_long);
+void dump_Elf_Rela(Obj_Entry *, const Elf_Rela *, u_long);
+
+/*
+ * Function declarations.
+ */
+const char *ld_get_env_var(int idx);
+uintptr_t rtld_round_page(uintptr_t);
+uintptr_t rtld_trunc_page(uintptr_t);
+Elf32_Word elf_hash(const char *);
+const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *,
+ const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *);
+void lockdflt_init(void);
+void digest_notes(Obj_Entry *, Elf_Addr, Elf_Addr);
+Obj_Entry *globallist_curr(const Obj_Entry *obj);
+Obj_Entry *globallist_next(const Obj_Entry *obj);
+void obj_free(Obj_Entry *);
+Obj_Entry *obj_new(void);
+Obj_Entry *obj_from_addr(const void *);
+void _rtld_bind_start(void);
+void *rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def);
+void symlook_init(SymLook *, const char *);
+int symlook_obj(SymLook *, const Obj_Entry *);
+void *tls_get_addr_common(struct tcb *tcb, int index, size_t offset);
+void *allocate_tls(Obj_Entry *, void *, size_t, size_t);
+void free_tls(void *, size_t, size_t);
+void *allocate_module_tls(struct tcb *tcb, int index);
+bool allocate_tls_offset(Obj_Entry *obj);
+void free_tls_offset(Obj_Entry *obj);
+const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long);
+int convert_prot(int elfflags);
+bool check_elf_headers(const Elf_Ehdr *hdr, const char *path);
+
+/*
+ * MD function declarations.
+ */
+int do_copy_relocations(Obj_Entry *);
+int reloc_non_plt(Obj_Entry *, Obj_Entry *, int flags,
+ struct Struct_RtldLockState *);
+int reloc_plt(Obj_Entry *, int flags, struct Struct_RtldLockState *);
+int reloc_jmpslots(Obj_Entry *, int flags, struct Struct_RtldLockState *);
+int reloc_iresolve(Obj_Entry *, struct Struct_RtldLockState *);
+int reloc_iresolve_nonplt(Obj_Entry *, struct Struct_RtldLockState *);
+int reloc_gnu_ifunc(Obj_Entry *, int flags, struct Struct_RtldLockState *);
+void ifunc_init(Elf_Auxinfo *[__min_size(AT_COUNT)]);
+void init_pltgot(Obj_Entry *);
+void allocate_initial_tls(Obj_Entry *);
+
+#endif /* } */
diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c
new file mode 100644
index 000000000000..d99b64d8c66a
--- /dev/null
+++ b/libexec/rtld-elf/rtld_lock.c
@@ -0,0 +1,505 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 1999, 2000 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: FreeBSD: src/libexec/rtld-elf/sparc64/lockdflt.c,v 1.3 2002/10/09
+ */
+
+/*
+ * Thread locking implementation for the dynamic linker.
+ *
+ * We use the "simple, non-scalable reader-preference lock" from:
+ *
+ * J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer
+ * Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on
+ * Principles and Practice of Parallel Programming, April 1991.
+ *
+ * In this algorithm the lock is a single word. Its low-order bit is
+ * set when a writer holds the lock. The remaining high-order bits
+ * contain a count of readers desiring the lock. The algorithm requires
+ * atomic "compare_and_store" and "add" operations, which we take
+ * from machine/atomic.h.
+ */
+
+#include <sys/param.h>
+#include <sys/signalvar.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "rtld_machdep.h"
+#include "rtld_libc.h"
+
+void _rtld_thread_init(struct RtldLockInfo *) __exported;
+void _rtld_atfork_pre(int *) __exported;
+void _rtld_atfork_post(int *) __exported;
+
+static char def_dlerror_msg[512];
+static int def_dlerror_seen_val = 1;
+
+static char *
+def_dlerror_loc(void)
+{
+ return (def_dlerror_msg);
+}
+
+static int *
+def_dlerror_seen(void)
+{
+ return (&def_dlerror_seen_val);
+}
+
+#define WAFLAG 0x1 /* A writer holds the lock */
+#define RC_INCR 0x2 /* Adjusts count of readers desiring lock */
+
+typedef struct Struct_Lock {
+ volatile u_int lock;
+ void *base;
+} Lock;
+
+static sigset_t fullsigmask, oldsigmask;
+static int thread_flag, wnested;
+static uint32_t fsigblock;
+
+static void *
+def_lock_create(void)
+{
+ void *base;
+ char *p;
+ uintptr_t r;
+ Lock *l;
+
+ /*
+ * Arrange for the lock to occupy its own cache line. First, we
+ * optimistically allocate just a cache line, hoping that malloc
+ * will give us a well-aligned block of memory. If that doesn't
+ * work, we allocate a larger block and take a well-aligned cache
+ * line from it.
+ */
+ base = xmalloc(CACHE_LINE_SIZE);
+ p = base;
+ if ((uintptr_t)p % CACHE_LINE_SIZE != 0) {
+ free(base);
+ base = xmalloc(2 * CACHE_LINE_SIZE);
+ p = base;
+ if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0)
+ p += CACHE_LINE_SIZE - r;
+ }
+ l = (Lock *)p;
+ l->base = base;
+ l->lock = 0;
+ return (l);
+}
+
+static void
+def_lock_destroy(void *lock)
+{
+ Lock *l = lock;
+
+ free(l->base);
+}
+
+static void
+sig_fastunblock(void)
+{
+ uint32_t oldval;
+
+ assert((fsigblock & ~SIGFASTBLOCK_FLAGS) >= SIGFASTBLOCK_INC);
+ oldval = atomic_fetchadd_32(&fsigblock, -SIGFASTBLOCK_INC);
+ if (oldval == (SIGFASTBLOCK_PEND | SIGFASTBLOCK_INC))
+ __sys_sigfastblock(SIGFASTBLOCK_UNBLOCK, NULL);
+}
+
+static bool
+def_lock_acquire_set(Lock *l, bool wlock)
+{
+ if (wlock) {
+ if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG))
+ return (true);
+ } else {
+ atomic_add_acq_int(&l->lock, RC_INCR);
+ if ((l->lock & WAFLAG) == 0)
+ return (true);
+ atomic_add_int(&l->lock, -RC_INCR);
+ }
+ return (false);
+}
+
+static void
+def_lock_acquire(Lock *l, bool wlock)
+{
+ sigset_t tmp_oldsigmask;
+
+ if (ld_fast_sigblock) {
+ for (;;) {
+ atomic_add_32(&fsigblock, SIGFASTBLOCK_INC);
+ if (def_lock_acquire_set(l, wlock))
+ break;
+ sig_fastunblock();
+ }
+ } else {
+ for (;;) {
+ sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
+ if (def_lock_acquire_set(l, wlock))
+ break;
+ sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
+ }
+ if (atomic_fetchadd_int(&wnested, 1) == 0)
+ oldsigmask = tmp_oldsigmask;
+ }
+}
+
+static void
+def_rlock_acquire(void *lock)
+{
+ def_lock_acquire(lock, false);
+}
+
+static void
+def_wlock_acquire(void *lock)
+{
+ def_lock_acquire(lock, true);
+}
+
+static void
+def_lock_release(void *lock)
+{
+ Lock *l = lock;
+
+ atomic_add_rel_int(&l->lock, -((l->lock & WAFLAG) == 0 ?
+ RC_INCR : WAFLAG));
+ if (ld_fast_sigblock)
+ sig_fastunblock();
+ else if (atomic_fetchadd_int(&wnested, -1) == 1)
+ sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
+}
+
+static int
+def_thread_set_flag(int mask)
+{
+ int old_val = thread_flag;
+
+ thread_flag |= mask;
+ return (old_val);
+}
+
+static int
+def_thread_clr_flag(int mask)
+{
+ int old_val = thread_flag;
+
+ thread_flag &= ~mask;
+ return (old_val);
+}
+
+/*
+ * Public interface exposed to the rest of the dynamic linker.
+ */
+struct RtldLockInfo lockinfo;
+static struct RtldLockInfo deflockinfo;
+
+static __inline int
+thread_mask_set(int mask)
+{
+ return (lockinfo.thread_set_flag(mask));
+}
+
+static __inline void
+thread_mask_clear(int mask)
+{
+ lockinfo.thread_clr_flag(mask);
+}
+
+#define RTLD_LOCK_CNT 3
+static struct rtld_lock {
+ void *handle;
+ int mask;
+} rtld_locks[RTLD_LOCK_CNT];
+
+rtld_lock_t rtld_bind_lock = &rtld_locks[0];
+rtld_lock_t rtld_libc_lock = &rtld_locks[1];
+rtld_lock_t rtld_phdr_lock = &rtld_locks[2];
+
+void
+rlock_acquire(rtld_lock_t lock, RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ if ((thread_mask_set(lock->mask) & lock->mask) != 0) {
+ dbg("rlock_acquire: recursed");
+ lockstate->lockstate = RTLD_LOCK_UNLOCKED;
+ return;
+ }
+ lockinfo.rlock_acquire(lock->handle);
+ lockstate->lockstate = RTLD_LOCK_RLOCKED;
+}
+
+void
+wlock_acquire(rtld_lock_t lock, RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ if ((thread_mask_set(lock->mask) & lock->mask) != 0) {
+ dbg("wlock_acquire: recursed");
+ lockstate->lockstate = RTLD_LOCK_UNLOCKED;
+ return;
+ }
+ lockinfo.wlock_acquire(lock->handle);
+ lockstate->lockstate = RTLD_LOCK_WLOCKED;
+}
+
+void
+lock_release(rtld_lock_t lock, RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ switch (lockstate->lockstate) {
+ case RTLD_LOCK_UNLOCKED:
+ break;
+ case RTLD_LOCK_RLOCKED:
+ case RTLD_LOCK_WLOCKED:
+ thread_mask_clear(lock->mask);
+ lockinfo.lock_release(lock->handle);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void
+lock_upgrade(rtld_lock_t lock, RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ lock_release(lock, lockstate);
+ wlock_acquire(lock, lockstate);
+}
+
+void
+lock_restart_for_upgrade(RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ switch (lockstate->lockstate) {
+ case RTLD_LOCK_UNLOCKED:
+ case RTLD_LOCK_WLOCKED:
+ break;
+ case RTLD_LOCK_RLOCKED:
+ siglongjmp(lockstate->env, 1);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+bool
+lockstate_wlocked(const RtldLockState *lockstate)
+{
+ return (lockstate->lockstate == RTLD_LOCK_WLOCKED);
+}
+
+void
+dlerror_dflt_init(void)
+{
+ lockinfo.dlerror_loc = def_dlerror_loc;
+ lockinfo.dlerror_loc_sz = sizeof(def_dlerror_msg);
+ lockinfo.dlerror_seen = def_dlerror_seen;
+}
+
+void
+lockdflt_init(void)
+{
+ int i;
+
+ deflockinfo.rtli_version = RTLI_VERSION;
+ deflockinfo.lock_create = def_lock_create;
+ deflockinfo.lock_destroy = def_lock_destroy;
+ deflockinfo.rlock_acquire = def_rlock_acquire;
+ deflockinfo.wlock_acquire = def_wlock_acquire;
+ deflockinfo.lock_release = def_lock_release;
+ deflockinfo.thread_set_flag = def_thread_set_flag;
+ deflockinfo.thread_clr_flag = def_thread_clr_flag;
+ deflockinfo.at_fork = NULL;
+ deflockinfo.dlerror_loc = def_dlerror_loc;
+ deflockinfo.dlerror_loc_sz = sizeof(def_dlerror_msg);
+ deflockinfo.dlerror_seen = def_dlerror_seen;
+
+ for (i = 0; i < RTLD_LOCK_CNT; i++) {
+ rtld_locks[i].mask = (1 << i);
+ rtld_locks[i].handle = NULL;
+ }
+
+ memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo));
+ _rtld_thread_init(NULL);
+ if (ld_fast_sigblock) {
+ __sys_sigfastblock(SIGFASTBLOCK_SETPTR, &fsigblock);
+ } else {
+ /*
+ * Construct a mask to block all signals. Note that
+ * blocked traps mean that the process is terminated
+ * if trap occurs while we are in locked section, with
+ * the default settings for kern.forcesigexit.
+ */
+ sigfillset(&fullsigmask);
+ }
+}
+
+/*
+ * Callback function to allow threads implementation to
+ * register their own locking primitives if the default
+ * one is not suitable.
+ * The current context should be the only context
+ * executing at the invocation time.
+ */
+void
+_rtld_thread_init(struct RtldLockInfo *pli)
+{
+ const Obj_Entry *obj;
+ SymLook req;
+ void *locks[RTLD_LOCK_CNT];
+ int flags, i, res;
+
+ if (pli == NULL) {
+ lockinfo.rtli_version = RTLI_VERSION;
+ } else {
+ lockinfo.rtli_version = RTLI_VERSION_ONE;
+ obj = obj_from_addr(pli->lock_create);
+ if (obj != NULL) {
+ symlook_init(&req, "_pli_rtli_version");
+ res = symlook_obj(&req, obj);
+ if (res == 0)
+ lockinfo.rtli_version = pli->rtli_version;
+ }
+ }
+
+ /* disable all locking while this function is running */
+ flags = thread_mask_set(~0);
+
+ if (pli == NULL)
+ pli = &deflockinfo;
+ else if (ld_fast_sigblock) {
+ fsigblock = 0;
+ __sys_sigfastblock(SIGFASTBLOCK_UNSETPTR, NULL);
+ }
+
+ for (i = 0; i < RTLD_LOCK_CNT; i++)
+ if ((locks[i] = pli->lock_create()) == NULL)
+ break;
+
+ if (i < RTLD_LOCK_CNT) {
+ while (--i >= 0)
+ pli->lock_destroy(locks[i]);
+ abort();
+ }
+
+ for (i = 0; i < RTLD_LOCK_CNT; i++) {
+ if (rtld_locks[i].handle == NULL)
+ continue;
+ if (flags & rtld_locks[i].mask)
+ lockinfo.lock_release(rtld_locks[i].handle);
+ lockinfo.lock_destroy(rtld_locks[i].handle);
+ }
+
+ for (i = 0; i < RTLD_LOCK_CNT; i++) {
+ rtld_locks[i].handle = locks[i];
+ if (flags & rtld_locks[i].mask)
+ pli->wlock_acquire(rtld_locks[i].handle);
+ }
+
+ lockinfo.lock_create = pli->lock_create;
+ lockinfo.lock_destroy = pli->lock_destroy;
+ lockinfo.rlock_acquire = pli->rlock_acquire;
+ lockinfo.wlock_acquire = pli->wlock_acquire;
+ lockinfo.lock_release = pli->lock_release;
+ lockinfo.thread_set_flag = pli->thread_set_flag;
+ lockinfo.thread_clr_flag = pli->thread_clr_flag;
+ lockinfo.at_fork = pli->at_fork;
+ if (lockinfo.rtli_version > RTLI_VERSION_ONE && pli != NULL) {
+ strlcpy(pli->dlerror_loc(), lockinfo.dlerror_loc(),
+ lockinfo.dlerror_loc_sz);
+ lockinfo.dlerror_loc = pli->dlerror_loc;
+ lockinfo.dlerror_loc_sz = pli->dlerror_loc_sz;
+ lockinfo.dlerror_seen = pli->dlerror_seen;
+ }
+
+ /* restore thread locking state, this time with new locks */
+ thread_mask_clear(~0);
+ thread_mask_set(flags);
+ dbg("_rtld_thread_init: done");
+}
+
+void
+_rtld_atfork_pre(int *locks)
+{
+ RtldLockState ls[2];
+
+ if (locks == NULL)
+ return;
+ bzero(ls, sizeof(ls));
+
+ /*
+ * Warning: this did not worked well with the rtld compat
+ * locks above, when the thread signal mask was corrupted (set
+ * to all signals blocked) if two locks were taken
+ * simultaneously in the write mode. The caller of the
+ * _rtld_atfork_pre() must provide the working implementation
+ * of the locks anyway, and libthr locks are fine.
+ */
+ if (ld_get_env_var(LD_NO_DL_ITERATE_PHDR_AFTER_FORK) == NULL)
+ wlock_acquire(rtld_phdr_lock, &ls[0]);
+ wlock_acquire(rtld_bind_lock, &ls[1]);
+
+ /* XXXKIB: I am really sorry for this. */
+ locks[0] = ls[1].lockstate;
+ locks[2] = ls[0].lockstate;
+}
+
+void
+_rtld_atfork_post(int *locks)
+{
+ RtldLockState ls[2];
+
+ if (locks == NULL)
+ return;
+
+ bzero(ls, sizeof(ls));
+ ls[0].lockstate = locks[2];
+ ls[1].lockstate = locks[0];
+ lock_release(rtld_bind_lock, &ls[1]);
+ if (ld_get_env_var(LD_NO_DL_ITERATE_PHDR_AFTER_FORK) == NULL)
+ lock_release(rtld_phdr_lock, &ls[0]);
+}
diff --git a/libexec/rtld-elf/rtld_lock.h b/libexec/rtld-elf/rtld_lock.h
new file mode 100644
index 000000000000..c9b11176b7de
--- /dev/null
+++ b/libexec/rtld-elf/rtld_lock.h
@@ -0,0 +1,102 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2003 Alexander Kabaev.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTLD_LOCK_H_
+#define _RTLD_LOCK_H_
+
+#define RTLI_VERSION_ONE 0x01
+#define RTLI_VERSION 0x02
+
+#define MAX_RTLD_LOCKS 8
+
+/*
+ * This structure is part of the ABI between rtld and threading
+ * libraries, like libthr and even libc_r. Its layout is fixed and
+ * can be changed only by appending new fields at the end, with the
+ * bump of RTLI_VERSION.
+ */
+struct RtldLockInfo
+{
+ /*
+ * Valid if the object calling _rtld_thread_init() exported
+ * symbol _pli_rtli_version. Otherwise assume RTLI_VERSION_ONE.
+ */
+ unsigned int rtli_version;
+
+ void *(*lock_create)(void);
+ void (*lock_destroy)(void *);
+ void (*rlock_acquire)(void *);
+ void (*wlock_acquire)(void *);
+ void (*lock_release)(void *);
+ int (*thread_set_flag)(int);
+ int (*thread_clr_flag)(int);
+ void (*at_fork)(void);
+
+ /* Version 2 fields */
+ char *(*dlerror_loc)(void);
+ int *(*dlerror_seen)(void);
+ int dlerror_loc_sz;
+};
+
+#if defined(IN_RTLD) || defined(PTHREAD_KERNEL)
+
+void _rtld_thread_init(struct RtldLockInfo *) __exported;
+void _rtld_atfork_pre(int *) __exported;
+void _rtld_atfork_post(int *) __exported;
+
+#endif /* IN_RTLD || PTHREAD_KERNEL */
+
+#ifdef IN_RTLD
+
+struct rtld_lock;
+typedef struct rtld_lock *rtld_lock_t;
+
+extern rtld_lock_t rtld_bind_lock;
+extern rtld_lock_t rtld_libc_lock;
+extern rtld_lock_t rtld_phdr_lock;
+
+extern struct RtldLockInfo lockinfo;
+
+#define RTLD_LOCK_UNLOCKED 0
+#define RTLD_LOCK_RLOCKED 1
+#define RTLD_LOCK_WLOCKED 2
+
+struct Struct_RtldLockState;
+typedef struct Struct_RtldLockState RtldLockState;
+
+void rlock_acquire(rtld_lock_t, RtldLockState *);
+void wlock_acquire(rtld_lock_t, RtldLockState *);
+void lock_release(rtld_lock_t, RtldLockState *);
+void lock_upgrade(rtld_lock_t, RtldLockState *);
+void lock_restart_for_upgrade(RtldLockState *);
+bool lockstate_wlocked(const RtldLockState *);
+
+void dlerror_dflt_init(void);
+
+#endif /* IN_RTLD */
+
+#endif
diff --git a/libexec/rtld-elf/rtld_malloc.c b/libexec/rtld-elf/rtld_malloc.c
new file mode 100644
index 000000000000..647718738ada
--- /dev/null
+++ b/libexec/rtld-elf/rtld_malloc.c
@@ -0,0 +1,322 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+/*
+ * malloc.c (Caltech) 2/21/82
+ * Chris Kingsley, kingsley@cit-20.
+ *
+ * This is a very fast storage allocator. It allocates blocks of a small
+ * number of different sizes, and keeps free lists of each size. Blocks that
+ * don't exactly fit are passed up to the next larger size. In this
+ * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long.
+ * This is designed for use in a virtual memory environment.
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef IN_RTLD
+#include "rtld.h"
+#include "rtld_printf.h"
+#include "rtld_paths.h"
+#endif
+#include "rtld_malloc.h"
+
+/*
+ * Pre-allocate mmap'ed pages
+ */
+#define NPOOLPAGES (128*1024/pagesz)
+static caddr_t pagepool_start, pagepool_end;
+
+/*
+ * The overhead on a block is at least 4 bytes. When free, this space
+ * contains a pointer to the next free block, and the bottom two bits must
+ * be zero. When in use, the first byte is set to MAGIC, and the second
+ * byte is the size index. The remaining bytes are for alignment.
+ */
+union overhead {
+ union overhead *ov_next; /* when free */
+ struct {
+ uint16_t ovu_index; /* bucket # */
+ uint8_t ovu_magic; /* magic number */
+ } ovu;
+#define ov_magic ovu.ovu_magic
+#define ov_index ovu.ovu_index
+};
+
+static void morecore(int bucket);
+static int morepages(int n);
+
+#define MAGIC 0xef /* magic # on accounting info */
+#define AMAGIC 0xdf /* magic # for aligned alloc */
+
+/*
+ * nextf[i] is the pointer to the next free block of size
+ * (FIRST_BUCKET_SIZE << i). The overhead information precedes the data
+ * area returned to the user.
+ */
+#define LOW_BITS 3
+#define FIRST_BUCKET_SIZE (1U << LOW_BITS)
+#define NBUCKETS 30
+static union overhead *nextf[NBUCKETS];
+
+static int pagesz; /* page size */
+
+/*
+ * The array of supported page sizes is provided by the user, i.e., the
+ * program that calls this storage allocator. That program must initialize
+ * the array before making its first call to allocate storage. The array
+ * must contain at least one page size. The page sizes must be stored in
+ * increasing order.
+ */
+
+static void *
+cp2op(void *cp)
+{
+ return (((caddr_t)cp - sizeof(union overhead)));
+}
+
+void *
+__crt_malloc(size_t nbytes)
+{
+ union overhead *op;
+ int bucket;
+ size_t amt;
+
+ /*
+ * First time malloc is called, setup page size.
+ */
+ if (pagesz == 0)
+ pagesz = pagesizes[0];
+ /*
+ * Convert amount of memory requested into closest block size
+ * stored in hash buckets which satisfies request.
+ * Account for space used per block for accounting.
+ */
+ amt = FIRST_BUCKET_SIZE;
+ bucket = 0;
+ while (nbytes > amt - sizeof(*op)) {
+ amt <<= 1;
+ bucket++;
+ if (amt == 0 || bucket >= NBUCKETS)
+ return (NULL);
+ }
+ /*
+ * If nothing in hash bucket right now,
+ * request more memory from the system.
+ */
+ if ((op = nextf[bucket]) == NULL) {
+ morecore(bucket);
+ if ((op = nextf[bucket]) == NULL)
+ return (NULL);
+ }
+ /* remove from linked list */
+ nextf[bucket] = op->ov_next;
+ op->ov_magic = MAGIC;
+ op->ov_index = bucket;
+ return ((char *)(op + 1));
+}
+
+void *
+__crt_calloc(size_t num, size_t size)
+{
+ void *ret;
+
+ if (size != 0 && (num * size) / size != num) {
+ /* size_t overflow. */
+ return (NULL);
+ }
+
+ if ((ret = __crt_malloc(num * size)) != NULL)
+ memset(ret, 0, num * size);
+
+ return (ret);
+}
+
+void *
+__crt_aligned_alloc_offset(size_t align, size_t size, size_t offset)
+{
+ void *mem, *ov;
+ union overhead ov1;
+ uintptr_t x;
+
+ if (align < FIRST_BUCKET_SIZE)
+ align = FIRST_BUCKET_SIZE;
+ offset &= align - 1;
+ mem = __crt_malloc(size + align + offset + sizeof(union overhead));
+ if (mem == NULL)
+ return (NULL);
+ x = roundup2((uintptr_t)mem + sizeof(union overhead), align);
+ x += offset;
+ ov = cp2op((void *)x);
+ ov1.ov_magic = AMAGIC;
+ ov1.ov_index = x - (uintptr_t)mem + sizeof(union overhead);
+ memcpy(ov, &ov1, sizeof(ov1));
+ return ((void *)x);
+}
+
+/*
+ * Allocate more memory to the indicated bucket.
+ */
+static void
+morecore(int bucket)
+{
+ union overhead *op;
+ int sz; /* size of desired block */
+ int amt; /* amount to allocate */
+ int nblks; /* how many blocks we get */
+
+ sz = FIRST_BUCKET_SIZE << bucket;
+ if (sz < pagesz) {
+ amt = pagesz;
+ nblks = amt / sz;
+ } else {
+ amt = sz;
+ nblks = 1;
+ }
+ if (amt > pagepool_end - pagepool_start)
+ if (morepages(amt / pagesz + NPOOLPAGES) == 0 &&
+ /* Retry with min required size */
+ morepages(amt / pagesz) == 0)
+ return;
+ op = (union overhead *)pagepool_start;
+ pagepool_start += amt;
+
+ /*
+ * Add new memory allocated to that on
+ * free list for this hash bucket.
+ */
+ nextf[bucket] = op;
+ while (--nblks > 0) {
+ op->ov_next = (union overhead *)((caddr_t)op + sz);
+ op = (union overhead *)((caddr_t)op + sz);
+ }
+}
+
+void
+__crt_free(void *cp)
+{
+ union overhead *op, op1;
+ void *opx;
+ int size;
+
+ if (cp == NULL)
+ return;
+ opx = cp2op(cp);
+ memcpy(&op1, opx, sizeof(op1));
+ op = op1.ov_magic == AMAGIC ? (void *)((caddr_t)cp - op1.ov_index) :
+ opx;
+ if (op->ov_magic != MAGIC)
+ return; /* sanity */
+ size = op->ov_index;
+ op->ov_next = nextf[size]; /* also clobbers ov_magic */
+ nextf[size] = op;
+}
+
+void *
+__crt_realloc(void *cp, size_t nbytes)
+{
+ u_int onb;
+ int i;
+ union overhead *op;
+ char *res;
+
+ if (cp == NULL)
+ return (__crt_malloc(nbytes));
+ op = cp2op(cp);
+ if (op->ov_magic != MAGIC)
+ return (NULL); /* Double-free or bad argument */
+ i = op->ov_index;
+ onb = 1 << (i + 3);
+ if (onb < (u_int)pagesz)
+ onb -= sizeof(*op);
+ else
+ onb += pagesz - sizeof(*op);
+ /* avoid the copy if same size block */
+ if (i != 0) {
+ i = 1 << (i + 2);
+ if (i < pagesz)
+ i -= sizeof(*op);
+ else
+ i += pagesz - sizeof(*op);
+ }
+ if (nbytes <= onb && nbytes > (size_t)i)
+ return (cp);
+ if ((res = __crt_malloc(nbytes)) == NULL)
+ return (NULL);
+ bcopy(cp, res, (nbytes < onb) ? nbytes : onb);
+ __crt_free(cp);
+ return (res);
+}
+
+static int
+morepages(int n)
+{
+ caddr_t addr;
+ int offset;
+
+ if (pagepool_end - pagepool_start > pagesz) {
+ addr = roundup2(pagepool_start, pagesz);
+ if (munmap(addr, pagepool_end - addr) != 0) {
+#ifdef IN_RTLD
+ rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": "
+ "morepages: cannot munmap %p: %s\n",
+ addr, rtld_strerror(errno));
+#endif
+ }
+ }
+
+ offset = (uintptr_t)pagepool_start - rounddown2(
+ (uintptr_t)pagepool_start, pagesz);
+
+ addr = mmap(0, n * pagesz, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (addr == MAP_FAILED) {
+#ifdef IN_RTLD
+ rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": morepages: "
+ "cannot mmap anonymous memory: %s\n",
+ rtld_strerror(errno));
+#endif
+ pagepool_start = pagepool_end = NULL;
+ return (0);
+ }
+ pagepool_start = addr;
+ pagepool_end = pagepool_start + n * pagesz;
+ pagepool_start += offset;
+
+ return (n);
+}
diff --git a/libexec/rtld-elf/rtld_malloc.h b/libexec/rtld-elf/rtld_malloc.h
new file mode 100644
index 000000000000..408cdc84d45e
--- /dev/null
+++ b/libexec/rtld-elf/rtld_malloc.h
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RTLD_MALLOC_H
+#define RTLD_MALLOC_H
+
+void *__crt_aligned_alloc_offset(size_t align, size_t size, size_t offset);
+void *__crt_calloc(size_t num, size_t size);
+void __crt_free(void *cp);
+void *__crt_malloc(size_t nbytes);
+void *__crt_realloc(void *cp, size_t nbytes);
+
+extern int npagesizes;
+extern size_t *pagesizes;
+
+#endif
diff --git a/libexec/rtld-elf/rtld_paths.h b/libexec/rtld-elf/rtld_paths.h
new file mode 100644
index 000000000000..c305b166c0b2
--- /dev/null
+++ b/libexec/rtld-elf/rtld_paths.h
@@ -0,0 +1,96 @@
+/*-
+ * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
+ * Copyright 2003 Alexander Kabaev <kan@FreeBSD.ORG>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTLD_PATHS_H
+#define _RTLD_PATHS_H
+
+#undef _PATH_ELF_HINTS
+
+#ifndef _RTLD_COMPAT_LIB_SUFFIX
+#ifdef COMPAT_libcompat
+#define _RTLD_COMPAT_LIB_SUFFIX COMPAT_libcompat
+#else
+#define _RTLD_COMPAT_LIB_SUFFIX ""
+#endif
+#endif
+
+#ifndef _RTLD_COMPAT_ENV_SUFFIX
+#ifdef COMPAT_LIBCOMPAT
+#define _RTLD_COMPAT_ENV_SUFFIX COMPAT_LIBCOMPAT "_"
+#else
+#define _RTLD_COMPAT_ENV_SUFFIX ""
+#endif
+#endif
+
+#ifndef __PATH_ELF_HINTS
+#define __PATH_ELF_HINTS(_lc) "/var/run/ld-elf" _lc ".so.hints"
+#endif
+
+#ifndef _PATH_ELF_HINTS
+#define _PATH_ELF_HINTS __PATH_ELF_HINTS(_RTLD_COMPAT_LIB_SUFFIX)
+#endif
+
+#ifndef _PATH_LIBMAP_CONF
+#define _PATH_LIBMAP_CONF "/etc/libmap" _RTLD_COMPAT_LIB_SUFFIX ".conf"
+#endif
+
+#ifndef __BASENAME_RTLD
+#define __BASENAME_RTLD(_lc) "ld-elf" _lc ".so.1"
+#endif
+
+#ifndef _BASENAME_RTLD
+#define _BASENAME_RTLD __BASENAME_RTLD(_RTLD_COMPAT_LIB_SUFFIX)
+#endif
+
+#ifndef __PATH_RTLD
+#define __PATH_RTLD(_lc) "/libexec/" __BASENAME_RTLD(_lc)
+#endif
+
+#ifndef _PATH_RTLD
+#define _PATH_RTLD __PATH_RTLD(_RTLD_COMPAT_LIB_SUFFIX)
+#endif
+
+#ifndef STANDARD_LIBRARY_PATH
+#define STANDARD_LIBRARY_PATH "/lib" _RTLD_COMPAT_LIB_SUFFIX ":/usr/lib" _RTLD_COMPAT_LIB_SUFFIX
+#endif
+
+#ifndef LD_
+#define LD_ "LD_" _RTLD_COMPAT_ENV_SUFFIX
+#endif
+
+#ifndef TOKEN_LIB
+#define TOKEN_LIB "lib" _RTLD_COMPAT_LIB_SUFFIX
+#endif
+
+#ifdef IN_RTLD
+extern const char *ld_elf_hints_default;
+extern const char *ld_path_libmap_conf;
+extern const char *ld_path_rtld;
+extern const char *ld_standard_library_path;
+extern const char *ld_env_prefix;
+#endif
+
+#endif /* _RTLD_PATHS_H */
diff --git a/libexec/rtld-elf/rtld_printf.c b/libexec/rtld-elf/rtld_printf.c
new file mode 100644
index 000000000000..69ee4e46fd1c
--- /dev/null
+++ b/libexec/rtld-elf/rtld_printf.c
@@ -0,0 +1,514 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ * Copyright (c) 2011 Konstantin Belousov <kib@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include "rtld_printf.h"
+#include "rtld_libc.h"
+
+#define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
+
+#define PRINT_METHOD_SNPRINTF 1
+#define PRINT_METHOD_WRITE 2
+
+struct snprintf_arg {
+ int method;
+ char *str;
+ char *buf;
+ size_t remain;
+ size_t buf_total;
+ int fd;
+};
+
+static void
+printf_out(struct snprintf_arg *info)
+{
+
+ if (info->remain == info->buf_total)
+ return;
+ write(info->fd, info->buf, info->buf_total - info->remain);
+ info->str = info->buf;
+ info->remain = info->buf_total;
+}
+
+static void
+snprintf_func(int ch, struct snprintf_arg *const info)
+{
+
+ switch (info->method) {
+ case PRINT_METHOD_SNPRINTF:
+ if (info->remain >= 2) {
+ *info->str++ = ch;
+ info->remain--;
+ }
+ break;
+ case PRINT_METHOD_WRITE:
+ if (info->remain == 0)
+ printf_out(info);
+ *info->str++ = ch;
+ info->remain--;
+ break;
+ }
+}
+
+static char const hex2ascii_lower[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+static char const hex2ascii_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+#define hex2ascii(hex) (hex2ascii_lower[hex])
+#define hex2ascii_upper(hex) (hex2ascii_upper[hex])
+
+static __inline int
+imax(int a, int b)
+{
+
+ return (a > b ? a : b);
+}
+
+static char *
+ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
+{
+ char *p, c;
+
+ p = nbuf;
+ *p = '\0';
+ do {
+ c = upper ? hex2ascii_upper(num % base) :
+ hex2ascii(num % base);
+ *++p = c;
+ } while (num /= base);
+ if (lenp)
+ *lenp = p - nbuf;
+ return (p);
+}
+
+static int
+kvprintf(char const *fmt, struct snprintf_arg *arg, int radix, va_list ap)
+{
+#define PCHAR(c) snprintf_func((c), arg)
+ char nbuf[MAXNBUF];
+ const char *p, *percent, *q;
+ u_char *up;
+ int ch, n, sign;
+ uintmax_t num;
+ int base, lflag, qflag, tmp, width, ladjust, sharpflag, dot;
+ int cflag, hflag, jflag, tflag, zflag;
+ int dwidth, upper;
+ char padc;
+ int stop = 0, retval = 0;
+
+ num = 0;
+
+ if (fmt == NULL)
+ fmt = "(fmt null)\n";
+
+ if (radix < 2 || radix > 36)
+ radix = 10;
+
+ for (;;) {
+ padc = ' ';
+ width = 0;
+ while ((ch = (u_char)*fmt++) != '%' || stop) {
+ if (ch == '\0')
+ return (retval);
+ PCHAR(ch);
+ }
+ percent = fmt - 1;
+ qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0;
+ sign = 0; dot = 0; dwidth = 0; upper = 0;
+ cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
+reswitch: switch (ch = (u_char)*fmt++) {
+ case '.':
+ dot = 1;
+ goto reswitch;
+ case '#':
+ sharpflag = 1;
+ goto reswitch;
+ case '+':
+ sign = '+';
+ goto reswitch;
+ case '-':
+ ladjust = 1;
+ goto reswitch;
+ case '%':
+ PCHAR(ch);
+ break;
+ case '*':
+ if (!dot) {
+ width = va_arg(ap, int);
+ if (width < 0) {
+ ladjust = !ladjust;
+ width = -width;
+ }
+ } else {
+ dwidth = va_arg(ap, int);
+ }
+ goto reswitch;
+ case '0':
+ if (!dot) {
+ padc = '0';
+ goto reswitch;
+ }
+ /* FALLTHROUGH */
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ for (n = 0;; ++fmt) {
+ n = n * 10 + ch - '0';
+ ch = *fmt;
+ if (ch < '0' || ch > '9')
+ break;
+ }
+ if (dot)
+ dwidth = n;
+ else
+ width = n;
+ goto reswitch;
+ case 'b':
+ num = (u_int)va_arg(ap, int);
+ p = va_arg(ap, char *);
+ for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
+ PCHAR(*q--);
+
+ if (num == 0)
+ break;
+
+ for (tmp = 0; *p;) {
+ n = *p++;
+ if (num & (1 << (n - 1))) {
+ PCHAR(tmp ? ',' : '<');
+ for (; (n = *p) > ' '; ++p)
+ PCHAR(n);
+ tmp = 1;
+ } else
+ for (; *p > ' '; ++p)
+ continue;
+ }
+ if (tmp)
+ PCHAR('>');
+ break;
+ case 'c':
+ PCHAR(va_arg(ap, int));
+ break;
+ case 'D':
+ up = va_arg(ap, u_char *);
+ p = va_arg(ap, char *);
+ if (!width)
+ width = 16;
+ while(width--) {
+ PCHAR(hex2ascii(*up >> 4));
+ PCHAR(hex2ascii(*up & 0x0f));
+ up++;
+ if (width)
+ for (q=p;*q;q++)
+ PCHAR(*q);
+ }
+ break;
+ case 'd':
+ case 'i':
+ base = 10;
+ goto handle_sign;
+ case 'h':
+ if (hflag) {
+ hflag = 0;
+ cflag = 1;
+ } else
+ hflag = 1;
+ goto reswitch;
+ case 'j':
+ jflag = 1;
+ goto reswitch;
+ case 'l':
+ if (lflag) {
+ lflag = 0;
+ qflag = 1;
+ } else
+ lflag = 1;
+ goto reswitch;
+ case 'n':
+ if (jflag)
+ *(va_arg(ap, intmax_t *)) = retval;
+ else if (qflag)
+ *(va_arg(ap, quad_t *)) = retval;
+ else if (lflag)
+ *(va_arg(ap, long *)) = retval;
+ else if (zflag)
+ *(va_arg(ap, size_t *)) = retval;
+ else if (hflag)
+ *(va_arg(ap, short *)) = retval;
+ else if (cflag)
+ *(va_arg(ap, char *)) = retval;
+ else
+ *(va_arg(ap, int *)) = retval;
+ break;
+ case 'o':
+ base = 8;
+ goto handle_nosign;
+ case 'p':
+ base = 16;
+ sharpflag = (width == 0);
+ sign = 0;
+ num = (uintptr_t)va_arg(ap, void *);
+ goto number;
+ case 'q':
+ qflag = 1;
+ goto reswitch;
+ case 'r':
+ base = radix;
+ if (sign) {
+ sign = 0;
+ goto handle_sign;
+ }
+ goto handle_nosign;
+ case 's':
+ p = va_arg(ap, char *);
+ if (p == NULL)
+ p = "(null)";
+ if (!dot)
+ n = strlen (p);
+ else
+ for (n = 0; n < dwidth && p[n]; n++)
+ continue;
+
+ width -= n;
+
+ if (!ladjust && width > 0)
+ while (width--)
+ PCHAR(padc);
+ while (n--)
+ PCHAR(*p++);
+ if (ladjust && width > 0)
+ while (width--)
+ PCHAR(padc);
+ break;
+ case 't':
+ tflag = 1;
+ goto reswitch;
+ case 'u':
+ base = 10;
+ goto handle_nosign;
+ case 'X':
+ upper = 1;
+ /* FALLTHROUGH */
+ case 'x':
+ base = 16;
+ goto handle_nosign;
+ case 'y':
+ base = 16;
+ goto handle_sign;
+ case 'z':
+ zflag = 1;
+ goto reswitch;
+handle_nosign:
+ if (jflag)
+ num = va_arg(ap, uintmax_t);
+ else if (qflag)
+ num = va_arg(ap, u_quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, u_long);
+ else if (zflag)
+ num = va_arg(ap, size_t);
+ else if (hflag)
+ num = (u_short)va_arg(ap, int);
+ else if (cflag)
+ num = (u_char)va_arg(ap, int);
+ else
+ num = va_arg(ap, u_int);
+ goto number;
+handle_sign:
+ if (jflag)
+ num = va_arg(ap, intmax_t);
+ else if (qflag)
+ num = va_arg(ap, quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, long);
+ else if (zflag)
+ num = va_arg(ap, ssize_t);
+ else if (hflag)
+ num = (short)va_arg(ap, int);
+ else if (cflag)
+ num = (signed char)va_arg(ap, int);
+ else
+ num = va_arg(ap, int);
+ if ((intmax_t)num < 0) {
+ sign = '-';
+ num = -(intmax_t)num;
+ }
+number:
+ p = ksprintn(nbuf, num, base, &n, upper);
+ tmp = 0;
+ if (sharpflag && num != 0) {
+ if (base == 8)
+ tmp++;
+ else if (base == 16)
+ tmp += 2;
+ }
+ if (sign)
+ tmp++;
+
+ if (!ladjust && padc == '0')
+ dwidth = width - tmp;
+ width -= tmp + imax(dwidth, n);
+ dwidth -= n;
+ if (!ladjust)
+ while (width-- > 0)
+ PCHAR(' ');
+ if (sign)
+ PCHAR(sign);
+ if (sharpflag && num != 0) {
+ if (base == 8) {
+ PCHAR('0');
+ } else if (base == 16) {
+ PCHAR('0');
+ PCHAR('x');
+ }
+ }
+ while (dwidth-- > 0)
+ PCHAR('0');
+
+ while (*p)
+ PCHAR(*p--);
+
+ if (ladjust)
+ while (width-- > 0)
+ PCHAR(' ');
+
+ break;
+ default:
+ while (percent < fmt)
+ PCHAR(*percent++);
+ /*
+ * Since we ignore an formatting argument it is no
+ * longer safe to obey the remaining formatting
+ * arguments as the arguments will no longer match
+ * the format specs.
+ */
+ stop = 1;
+ break;
+ }
+ }
+#undef PCHAR
+}
+
+int
+rtld_snprintf(char *buf, size_t bufsize, const char *fmt, ...)
+{
+ va_list ap;
+ int retval;
+
+ va_start(ap, fmt);
+ retval = rtld_vsnprintf(buf, bufsize, fmt, ap);
+ va_end(ap);
+ return (retval);
+}
+
+int
+rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap)
+{
+ struct snprintf_arg info;
+ int retval;
+
+ info.method = PRINT_METHOD_SNPRINTF;
+ info.buf = info.str = buf;
+ info.buf_total = info.remain = bufsize;
+ info.fd = -1;
+ retval = kvprintf(fmt, &info, 10, ap);
+ if (info.remain >= 1)
+ *info.str++ = '\0';
+ return (retval);
+}
+
+int
+rtld_vfdprintf(int fd, const char *fmt, va_list ap)
+{
+ char buf[512];
+ struct snprintf_arg info;
+ int retval;
+
+ info.method = PRINT_METHOD_WRITE;
+ info.buf = info.str = buf;
+ info.buf_total = info.remain = sizeof(buf);
+ info.fd = fd;
+ retval = kvprintf(fmt, &info, 10, ap);
+ printf_out(&info);
+ return (retval);
+}
+
+int
+rtld_fdprintf(int fd, const char *fmt, ...)
+{
+ va_list ap;
+ int retval;
+
+ va_start(ap, fmt);
+ retval = rtld_vfdprintf(fd, fmt, ap);
+ va_end(ap);
+ return (retval);
+}
+
+int
+rtld_fdprintfx(int fd, const char *fmt, ...)
+{
+ va_list ap;
+ int retval;
+
+ va_start(ap, fmt);
+ retval = rtld_vfdprintf(fd, fmt, ap);
+ va_end(ap);
+ return (retval);
+}
+
+void
+rtld_fdputstr(int fd, const char *str)
+{
+
+ write(fd, str, strlen(str));
+}
+
+void
+rtld_fdputchar(int fd, int c)
+{
+ char c1;
+
+ c1 = c;
+ write(fd, &c1, 1);
+}
diff --git a/libexec/rtld-elf/rtld_printf.h b/libexec/rtld-elf/rtld_printf.h
new file mode 100644
index 000000000000..131434e773ab
--- /dev/null
+++ b/libexec/rtld-elf/rtld_printf.h
@@ -0,0 +1,48 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2011 Konstantin Belousov <kib@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RTLD_PRINTF_H
+#define RTLD_PRINTF_H 1
+
+#include <sys/cdefs.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+int rtld_snprintf(char *buf, size_t bufsize, const char *fmt, ...)
+ __printflike(3, 4);
+int rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap);
+int rtld_vfdprintf(int fd, const char *fmt, va_list ap);
+int rtld_fdprintf(int fd, const char *fmt, ...) __printflike(2, 3);
+int rtld_fdprintfx(int fd, const char *fmt, ...);
+void rtld_fdputstr(int fd, const char *str);
+void rtld_fdputchar(int fd, int c);
+
+#define rtld_printf(...) rtld_fdprintf(STDOUT_FILENO, __VA_ARGS__)
+#define rtld_putstr(str) rtld_fdputstr(STDOUT_FILENO, (str))
+#define rtld_putchar(c) rtld_fdputchar(STDOUT_FILENO, (c))
+
+#endif
diff --git a/libexec/rtld-elf/rtld_tls.h b/libexec/rtld-elf/rtld_tls.h
new file mode 100644
index 000000000000..56c30fa4977e
--- /dev/null
+++ b/libexec/rtld-elf/rtld_tls.h
@@ -0,0 +1,69 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Semi-public interface from thread libraries to rtld for managing
+ * TLS.
+ */
+
+#ifndef _RTLD_TLS_H_
+#define _RTLD_TLS_H_
+
+/*
+ * Allocate a TLS block for a new thread. The memory allocated will
+ * include 'tcbsize' bytes aligned to a 'tcbalign' boundary (in bytes)
+ * for the thread library's private purposes. The location of the TCB
+ * block is returned by this function. For architectures using
+ * 'Variant I' TLS, the thread local storage follows the TCB, and for
+ * 'Variant II', the thread local storage precedes it. For
+ * architectures using the 'Variant II' model (e.g. i386, amd64) the
+ * TCB must begin with two pointer fields which are used by rtld for
+ * its TLS implementation. For the 'Variant I' model, the TCB must
+ * begin with a single pointer field for rtld's implementation.
+ *
+ * If the value of 'oldtls' is non-NULL, the new TLS block will be
+ * initialised using the values contained in 'oldtls' and 'oldtls'
+ * will be freed. This is typically used when initialising a thread
+ * library to migrate from using the initial bootstrap TLS block
+ * created by rtld to one which contains suitable thread library
+ * private data.
+ *
+ * The value returned from this function is suitable for installing
+ * directly into the thread pointer register.
+ */
+void *_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
+ __exported;
+
+/*
+ * Free a TLS block allocated using _rtld_allocate_tls(). The tcbsize
+ * and tcbalign parameters must be the same as those used to allocate
+ * the block.
+ */
+void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign) __exported;
+
+#endif
diff --git a/libexec/rtld-elf/rtld_utrace.h b/libexec/rtld-elf/rtld_utrace.h
new file mode 100644
index 000000000000..c14a62ddcb0b
--- /dev/null
+++ b/libexec/rtld-elf/rtld_utrace.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2007 John Baldwin
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RTLD_UTRACE_H
+#define RTLD_UTRACE_H
+
+#include <sys/param.h>
+
+#define UTRACE_DLOPEN_START 1
+#define UTRACE_DLOPEN_STOP 2
+#define UTRACE_DLCLOSE_START 3
+#define UTRACE_DLCLOSE_STOP 4
+#define UTRACE_LOAD_OBJECT 5
+#define UTRACE_UNLOAD_OBJECT 6
+#define UTRACE_ADD_RUNDEP 7
+#define UTRACE_PRELOAD_FINISHED 8
+#define UTRACE_INIT_CALL 9
+#define UTRACE_FINI_CALL 10
+#define UTRACE_DLSYM_START 11
+#define UTRACE_DLSYM_STOP 12
+#define UTRACE_RTLD_ERROR 13
+
+#define RTLD_UTRACE_SIG_SZ 4
+#define RTLD_UTRACE_SIG "RTLD"
+
+struct utrace_rtld {
+ char sig[RTLD_UTRACE_SIG_SZ];
+ int event;
+ void *handle;
+ void *mapbase; /* Used for 'parent' and 'init/fini' */
+ size_t mapsize;
+ int refcnt; /* Used for 'mode' */
+ char name[MAXPATHLEN];
+};
+
+#endif
diff --git a/libexec/rtld-elf/tests/Makefile b/libexec/rtld-elf/tests/Makefile
new file mode 100644
index 000000000000..c4b3baab4cb8
--- /dev/null
+++ b/libexec/rtld-elf/tests/Makefile
@@ -0,0 +1,19 @@
+SUBDIR+= libpythagoras libdeep libval libval2 target
+TESTS_SUBDIRS+= rtld_deepbind
+
+SUBDIR_DEPEND_libdeep= libval2
+SUBDIR_DEPEND_rtld_deepbind= libval
+SUBDIR_DEPEND_target= libpythagoras
+
+ATF_TESTS_C= ld_library_pathfds
+ATF_TESTS_C+= ld_preload_fds
+
+.for t in ${ATF_TESTS_C}
+SRCS.$t= $t.c common.c
+.endfor
+
+ATF_TESTS_C+= dlopen_test
+
+WARNS?= 3
+
+.include <bsd.test.mk>
diff --git a/libexec/rtld-elf/tests/Makefile.depend b/libexec/rtld-elf/tests/Makefile.depend
new file mode 100644
index 000000000000..1af0c88e099c
--- /dev/null
+++ b/libexec/rtld-elf/tests/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rtld-elf/tests/Makefile.inc b/libexec/rtld-elf/tests/Makefile.inc
new file mode 100644
index 000000000000..3bd0b8590cdc
--- /dev/null
+++ b/libexec/rtld-elf/tests/Makefile.inc
@@ -0,0 +1,3 @@
+PACKAGE?= tests
+NO_DEV_PACKAGE=
+TESTSDIR?= ${TESTSBASE}/libexec/rtld-elf
diff --git a/libexec/rtld-elf/tests/common.c b/libexec/rtld-elf/tests/common.c
new file mode 100644
index 000000000000..b878e8284e07
--- /dev/null
+++ b/libexec/rtld-elf/tests/common.c
@@ -0,0 +1,79 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ * Copyright 2014 Jonathan Anderson.
+ * Copyright 2021 Mariusz Zaborski <oshogbo@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "common.h"
+
+void
+expect_success(int binary, char *senv)
+{
+ char * const env[] = { senv, NULL };
+
+ try_to_run(binary, 0, env, "the hypotenuse of 3 and 4 is 5\n", "");
+}
+
+void
+expect_missing_library(int binary, char *senv)
+{
+ char * const env[] = { senv, NULL };
+
+ try_to_run(binary, 1, env, "",
+ "ld-elf.so.1: Shared object \"libpythagoras.so.0\" not found,"
+ " required by \"target\"\n");
+}
+
+void
+try_to_run(int binary, int exit_status, char * const *env,
+ const char *expected_out, const char *expected_err)
+{
+ pid_t child = atf_utils_fork();
+
+ if (child == 0) {
+ char * const args[] = { "target", NULL };
+
+ fexecve(binary, args, env);
+ atf_tc_fail("fexecve() failed");
+ }
+
+ atf_utils_wait(child, exit_status, expected_out, expected_err);
+}
+
+int
+opendir_fd(const char *name)
+{
+
+ return open(name, O_RDONLY | O_DIRECTORY);
+}
+
+int
+opendirat(int parent, const char *name)
+{
+
+ return openat(parent, name, O_RDONLY | O_DIRECTORY);
+}
diff --git a/libexec/rtld-elf/tests/common.h b/libexec/rtld-elf/tests/common.h
new file mode 100644
index 000000000000..7465305897d6
--- /dev/null
+++ b/libexec/rtld-elf/tests/common.h
@@ -0,0 +1,41 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ * Copyright 2014 Jonathan Anderson.
+ * Copyright 2021 Mariusz Zaborski <oshogbo@vexillium.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LD_COMMON_H_
+#define _LD_COMMON_H_
+
+#define TARGET_ELF_NAME "target"
+#define TARGET_LIBRARY "libpythagoras.so.0"
+
+void expect_success(int binary, char *senv);
+void expect_missing_library(int binary, char *senv);
+
+void try_to_run(int binary, int expected_exit_status, char * const *env,
+ const char *expected_out, const char *expected_err);
+int opendir_fd(const char *name);
+int opendirat(int parent, const char *name);
+
+#endif /* _LD_COMMON_H_ */
diff --git a/libexec/rtld-elf/tests/dlopen_test.c b/libexec/rtld-elf/tests/dlopen_test.c
new file mode 100644
index 000000000000..ab1e8da1cb41
--- /dev/null
+++ b/libexec/rtld-elf/tests/dlopen_test.c
@@ -0,0 +1,52 @@
+/*-
+ *
+ * Copyright (C) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ */
+
+#include <dlfcn.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(dlopen_basic);
+ATF_TC_BODY(dlopen_basic, tc)
+{
+ void *hdl, *sym;
+
+ hdl = dlopen("libthr.so", RTLD_NOW);
+ ATF_REQUIRE(hdl != NULL);
+
+ sym = dlsym(hdl, "pthread_create");
+ ATF_REQUIRE(sym != NULL);
+
+ dlclose(hdl);
+
+ sym = dlsym(hdl, "pthread_create");
+ ATF_REQUIRE(sym == NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(dlopen_recursing);
+ATF_TC_BODY(dlopen_recursing, tc)
+{
+ void *hdl;
+
+ /*
+ * If this doesn't crash, we're OK; a regression at one point caused
+ * some infinite recursion here.
+ */
+ hdl = dlopen("libthr.so", RTLD_NOW | RTLD_GLOBAL);
+ ATF_REQUIRE(hdl != NULL);
+
+ dlclose(hdl);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, dlopen_basic);
+ ATF_TP_ADD_TC(tp, dlopen_recursing);
+
+ return atf_no_error();
+}
diff --git a/libexec/rtld-elf/tests/ld_library_pathfds.c b/libexec/rtld-elf/tests/ld_library_pathfds.c
new file mode 100644
index 000000000000..0d0feb9a0f74
--- /dev/null
+++ b/libexec/rtld-elf/tests/ld_library_pathfds.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright 2014 Jonathan Anderson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "common.h"
+
+struct descriptors {
+ int binary;
+ int testdir;
+ int root;
+ int etc;
+ int usr;
+};
+
+
+static void setup(struct descriptors *, const atf_tc_t *);
+
+
+ATF_TC_WITHOUT_HEAD(missing_library);
+ATF_TC_BODY(missing_library, tc)
+{
+ struct descriptors files;
+
+ setup(&files, tc);
+ expect_missing_library(files.binary, NULL);
+}
+
+
+ATF_TC_WITHOUT_HEAD(wrong_library_directories);
+ATF_TC_BODY(wrong_library_directories, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d", files.etc) > 0);
+
+ expect_missing_library(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(bad_library_directories);
+ATF_TC_BODY(bad_library_directories, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=::") > 0);
+
+ expect_missing_library(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(single_library_directory);
+ATF_TC_BODY(single_library_directory, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d", files.testdir) > 0);
+
+ expect_success(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(first_library_directory);
+ATF_TC_BODY(first_library_directory, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d:%d",
+ files.testdir, files.etc) > 0);
+
+ expect_success(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(middle_library_directory);
+ATF_TC_BODY(middle_library_directory, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d:%d:%d",
+ files.root, files.testdir, files.usr) > 0);
+
+ expect_success(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(last_library_directory);
+ATF_TC_BODY(last_library_directory, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d:%d",
+ files.root, files.testdir) > 0);
+
+ expect_success(files.binary, pathfds);
+}
+
+
+
+/* Register test cases with ATF. */
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, missing_library);
+ ATF_TP_ADD_TC(tp, wrong_library_directories);
+ ATF_TP_ADD_TC(tp, bad_library_directories);
+ ATF_TP_ADD_TC(tp, single_library_directory);
+ ATF_TP_ADD_TC(tp, first_library_directory);
+ ATF_TP_ADD_TC(tp, middle_library_directory);
+ ATF_TP_ADD_TC(tp, last_library_directory);
+
+ return atf_no_error();
+}
+
+
+static void
+setup(struct descriptors *dp, const atf_tc_t *tc)
+{
+
+ dp->testdir = opendir_fd(atf_tc_get_config_var(tc, "srcdir"));
+ ATF_REQUIRE(dp->testdir >= 0);
+ ATF_REQUIRE(
+ (dp->binary = openat(dp->testdir, TARGET_ELF_NAME, O_RDONLY)) >= 0);
+
+ ATF_REQUIRE((dp->root = opendir_fd("/")) >= 0);
+ ATF_REQUIRE((dp->etc = opendirat(dp->root, "etc")) >= 0);
+ ATF_REQUIRE((dp->usr = opendirat(dp->root, "usr")) >= 0);
+}
+
diff --git a/libexec/rtld-elf/tests/ld_preload_fds.c b/libexec/rtld-elf/tests/ld_preload_fds.c
new file mode 100644
index 000000000000..ce620c6b1d2a
--- /dev/null
+++ b/libexec/rtld-elf/tests/ld_preload_fds.c
@@ -0,0 +1,106 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ * Copyright 2021 Mariusz Zaborski <oshogbo@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "common.h"
+
+int binaryfd;
+int libraryfd;
+
+static void
+setup(const atf_tc_t *tc)
+{
+ int testdir;
+
+ testdir = opendir_fd(atf_tc_get_config_var(tc, "srcdir"));
+ ATF_REQUIRE(testdir >= 0);
+
+ binaryfd = openat(testdir, TARGET_ELF_NAME, O_RDONLY);
+ ATF_REQUIRE(binaryfd >= 0);
+ libraryfd = openat(testdir, TARGET_LIBRARY, O_RDONLY);
+ ATF_REQUIRE(libraryfd >= 0);
+
+ close(testdir);
+}
+
+ATF_TC_WITHOUT_HEAD(missing_library);
+ATF_TC_BODY(missing_library, tc)
+{
+
+ setup(tc);
+ expect_missing_library(binaryfd, NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(bad_librarys);
+ATF_TC_BODY(bad_librarys, tc)
+{
+ char *senv;
+
+ ATF_REQUIRE(asprintf(&senv, "LD_PRELOAD_FDS=::") > 0);
+
+ setup(tc);
+ expect_missing_library(binaryfd, senv);
+}
+
+ATF_TC_WITHOUT_HEAD(single_library);
+ATF_TC_BODY(single_library, tc)
+{
+ char *senv;
+
+ setup(tc);
+
+ ATF_REQUIRE(
+ asprintf(&senv, "LD_PRELOAD_FDS=%d", libraryfd) > 0);
+
+ expect_success(binaryfd, senv);
+}
+
+ATF_TC_WITHOUT_HEAD(two_librarys);
+ATF_TC_BODY(two_librarys, tc)
+{
+ char *senv;
+
+ setup(tc);
+
+ ATF_REQUIRE(
+ asprintf(&senv, "LD_PRELOAD_FDS=%d:%d", libraryfd, libraryfd) > 0);
+
+ expect_success(binaryfd, senv);
+}
+
+/* Register test cases with ATF. */
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, missing_library);
+ ATF_TP_ADD_TC(tp, bad_librarys);
+ ATF_TP_ADD_TC(tp, single_library);
+ ATF_TP_ADD_TC(tp, two_librarys);
+
+ return atf_no_error();
+}
diff --git a/libexec/rtld-elf/tests/libdeep/Makefile b/libexec/rtld-elf/tests/libdeep/Makefile
new file mode 100644
index 000000000000..5b8e47e12291
--- /dev/null
+++ b/libexec/rtld-elf/tests/libdeep/Makefile
@@ -0,0 +1,14 @@
+SHLIB?= deep
+SHLIB_MAJOR= 0
+
+LIBDIR= ${TESTSBASE}/libexec/rtld-elf/rtld_deepbind
+SHLIBDIR= ${TESTSBASE}/libexec/rtld-elf/rtld_deepbind
+
+SRCS= libdeep.c
+
+LIBVAL2= ${.OBJDIR}/../libval2
+LDFLAGS+= -L${LIBVAL2} -Wl,-rpath,'$$ORIGIN'
+DPADD+= -lval2
+LDADD+= -lval2
+
+.include <bsd.lib.mk>
diff --git a/libexec/rtld-elf/tests/libdeep/libdeep.c b/libexec/rtld-elf/tests/libdeep/libdeep.c
new file mode 100644
index 000000000000..e570769300cf
--- /dev/null
+++ b/libexec/rtld-elf/tests/libdeep/libdeep.c
@@ -0,0 +1,28 @@
+/*-
+ *
+ * Copyright (C) 2023 NetApp, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ */
+
+#include <stdio.h>
+
+int get_value(void);
+int proxy_get_value(void);
+void set_value(int);
+void proxy_set_value(int);
+
+int
+proxy_get_value(void)
+{
+
+ return (get_value());
+}
+
+void
+proxy_set_value(int val)
+{
+
+ return (set_value(val));
+}
diff --git a/libexec/rtld-elf/tests/libpythagoras/Makefile b/libexec/rtld-elf/tests/libpythagoras/Makefile
new file mode 100644
index 000000000000..0e3fa37acf8a
--- /dev/null
+++ b/libexec/rtld-elf/tests/libpythagoras/Makefile
@@ -0,0 +1,13 @@
+.include <bsd.own.mk>
+
+LIB= pythagoras
+SHLIB_MAJOR= 0
+
+LIBDIR= ${TESTSBASE}/libexec/rtld-elf
+SHLIBDIR= ${TESTSBASE}/libexec/rtld-elf
+
+SRCS= pythagoras.c
+
+LIBADD= m
+
+.include <bsd.lib.mk>
diff --git a/libexec/rtld-elf/tests/libpythagoras/Makefile.depend b/libexec/rtld-elf/tests/libpythagoras/Makefile.depend
new file mode 100644
index 000000000000..f4be5a66c350
--- /dev/null
+++ b/libexec/rtld-elf/tests/libpythagoras/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rtld-elf/tests/libpythagoras/pythagoras.c b/libexec/rtld-elf/tests/libpythagoras/pythagoras.c
new file mode 100644
index 000000000000..54bc7457c2d6
--- /dev/null
+++ b/libexec/rtld-elf/tests/libpythagoras/pythagoras.c
@@ -0,0 +1,40 @@
+/*-
+ * Copyright 2014 Jonathan Anderson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <math.h>
+
+#include "pythagoras.h"
+
+double
+pythagorean_theorem(double a, double b)
+{
+
+ if (a <= 0 || b <= 0) {
+ errno = ERANGE;
+ return (-1.0);
+ }
+ return (sqrt(pow(a, 2) + pow(b, 2)));
+}
diff --git a/libexec/rtld-elf/tests/libpythagoras/pythagoras.h b/libexec/rtld-elf/tests/libpythagoras/pythagoras.h
new file mode 100644
index 000000000000..4c5bcd774011
--- /dev/null
+++ b/libexec/rtld-elf/tests/libpythagoras/pythagoras.h
@@ -0,0 +1,26 @@
+/*-
+ * Copyright 2014 Jonathan Anderson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+double pythagorean_theorem(double, double);
diff --git a/libexec/rtld-elf/tests/libval/Makefile b/libexec/rtld-elf/tests/libval/Makefile
new file mode 100644
index 000000000000..913564e9ad0f
--- /dev/null
+++ b/libexec/rtld-elf/tests/libval/Makefile
@@ -0,0 +1,9 @@
+SHLIB?= val
+SHLIB_MAJOR= 0
+
+LIBDIR= ${TESTSBASE}/libexec/rtld-elf/rtld_deepbind
+SHLIBDIR= ${TESTSBASE}/libexec/rtld-elf/rtld_deepbind
+
+SRCS= libval.c
+
+.include <bsd.lib.mk>
diff --git a/libexec/rtld-elf/tests/libval/libval.c b/libexec/rtld-elf/tests/libval/libval.c
new file mode 100644
index 000000000000..97c97a0310a7
--- /dev/null
+++ b/libexec/rtld-elf/tests/libval/libval.c
@@ -0,0 +1,26 @@
+/*-
+ *
+ * Copyright (C) 2023 NetApp, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ */
+
+static int val;
+
+int get_value(void);
+void set_value(int);
+
+int
+get_value(void)
+{
+
+ return (val);
+}
+
+void
+set_value(int nval)
+{
+
+ val = nval;
+}
diff --git a/libexec/rtld-elf/tests/libval2/Makefile b/libexec/rtld-elf/tests/libval2/Makefile
new file mode 100644
index 000000000000..f353a248b1b8
--- /dev/null
+++ b/libexec/rtld-elf/tests/libval2/Makefile
@@ -0,0 +1,7 @@
+LIBVAL= ${.CURDIR}/../libval
+
+# Just rebuild libval
+.PATH: ${LIBVAL:tA}
+SHLIB?= val2
+
+.include "${LIBVAL}/Makefile"
diff --git a/libexec/rtld-elf/tests/rtld_deepbind/Makefile b/libexec/rtld-elf/tests/rtld_deepbind/Makefile
new file mode 100644
index 000000000000..7054a846449d
--- /dev/null
+++ b/libexec/rtld-elf/tests/rtld_deepbind/Makefile
@@ -0,0 +1,9 @@
+TESTSDIR?= ${TESTSBASE}/libexec/rtld-elf/rtld_deepbind
+ATF_TESTS_C= rtld_deepbind
+
+LIBVAL= ${.OBJDIR}/../libval
+LDFLAGS.rtld_deepbind+= -L${LIBVAL} -Wl,-rpath,'$$ORIGIN'
+DPADD+= -lval
+LDADD+= -lval
+
+.include <bsd.test.mk>
diff --git a/libexec/rtld-elf/tests/rtld_deepbind/rtld_deepbind.c b/libexec/rtld-elf/tests/rtld_deepbind/rtld_deepbind.c
new file mode 100644
index 000000000000..4fe3c185982a
--- /dev/null
+++ b/libexec/rtld-elf/tests/rtld_deepbind/rtld_deepbind.c
@@ -0,0 +1,65 @@
+/*-
+ *
+ * Copyright (C) 2023 NetApp, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ */
+
+#include <dlfcn.h>
+
+#include <atf-c.h>
+
+int get_value(void);
+void set_value(int);
+
+#define APP_VALUE 5
+#define LIB_VALUE 20
+
+ATF_TC_WITHOUT_HEAD(deepbind_simple);
+ATF_TC_BODY(deepbind_simple, tc)
+{
+ void *hdl;
+ void (*proxy_set_value)(int);
+ int (*proxy_get_value)(void);
+ int app_value, lib_value;
+
+ set_value(APP_VALUE);
+
+ /*
+ * libdeep has a dependency on libval2.so, which is a rebuild of
+ * libval.so that provides get_value() and set_value() for both us and
+ * the lib. The lib's get_value() and set_value() should bind to the
+ * versions in libval2 instead of libval with RTLD_DEEPBIND.
+ */
+ hdl = dlopen("$ORIGIN/libdeep.so", RTLD_LAZY | RTLD_DEEPBIND);
+ ATF_REQUIRE(hdl != NULL);
+
+ proxy_set_value = dlsym(hdl, "proxy_set_value");
+ ATF_REQUIRE(proxy_set_value != NULL);
+
+ proxy_get_value = dlsym(hdl, "proxy_get_value");
+ ATF_REQUIRE(proxy_get_value != NULL);
+
+ (*proxy_set_value)(LIB_VALUE);
+
+ lib_value = (*proxy_get_value)();
+ app_value = get_value();
+
+ /*
+ * In the initial implementation or if libdeep.so is *not* linked
+ * against its own libval2, then these both return the later set
+ * LIB_VALUE (20) as they bind to the symbol provided by libval and
+ * use its .bss val.
+ */
+ ATF_REQUIRE_INTEQ(lib_value, LIB_VALUE);
+ ATF_REQUIRE_INTEQ(app_value, APP_VALUE);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, deepbind_simple);
+
+ return atf_no_error();
+}
diff --git a/libexec/rtld-elf/tests/target/Makefile b/libexec/rtld-elf/tests/target/Makefile
new file mode 100644
index 000000000000..8069229dedd8
--- /dev/null
+++ b/libexec/rtld-elf/tests/target/Makefile
@@ -0,0 +1,15 @@
+.include <bsd.own.mk>
+
+PROG= target
+BINDIR= ${TESTSDIR}
+
+WARNS?= 3
+CFLAGS+= -I${.CURDIR}/../libpythagoras
+
+LDFLAGS+= -L${.OBJDIR}/../libpythagoras
+DPADD+= ${.OBJDIR}/../libpythagoras/libpythagoras.a
+LDADD= -lpythagoras
+
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/libexec/rtld-elf/tests/target/Makefile.depend b/libexec/rtld-elf/tests/target/Makefile.depend
new file mode 100644
index 000000000000..a1a93996c771
--- /dev/null
+++ b/libexec/rtld-elf/tests/target/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ libexec/rtld-elf/tests/libpythagoras \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/rtld-elf/tests/target/target.c b/libexec/rtld-elf/tests/target/target.c
new file mode 100644
index 000000000000..8ec19f53c1b5
--- /dev/null
+++ b/libexec/rtld-elf/tests/target/target.c
@@ -0,0 +1,37 @@
+/*-
+ * Copyright 2014 Jonathan Anderson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pythagoras.h"
+
+#include <stdio.h>
+
+int
+main(int argc, char *argv[])
+{
+ float hypotenuse = pythagorean_theorem(3, 4);
+ printf("the hypotenuse of 3 and 4 is %d\n", (int) hypotenuse);
+
+ return 0;
+}
diff --git a/libexec/rtld-elf/xmalloc.c b/libexec/rtld-elf/xmalloc.c
new file mode 100644
index 000000000000..5f7c1b5ba10a
--- /dev/null
+++ b/libexec/rtld-elf/xmalloc.c
@@ -0,0 +1,92 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "rtld.h"
+#include "rtld_printf.h"
+#include "rtld_malloc.h"
+#include "rtld_libc.h"
+
+void *
+xcalloc(size_t number, size_t size)
+{
+ void *p;
+
+ p = __crt_calloc(number, size);
+ if (p == NULL) {
+ rtld_fdputstr(STDERR_FILENO, "Out of memory\n");
+ _exit(1);
+ }
+ return (p);
+}
+
+void *
+xmalloc(size_t size)
+{
+
+ void *p;
+
+ p = __crt_malloc(size);
+ if (p == NULL) {
+ rtld_fdputstr(STDERR_FILENO, "Out of memory\n");
+ _exit(1);
+ }
+ return (p);
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *copy;
+ size_t len;
+
+ len = strlen(str) + 1;
+ copy = xmalloc(len);
+ memcpy(copy, str, len);
+ return (copy);
+}
+
+void *
+xmalloc_aligned(size_t size, size_t align, size_t offset)
+{
+ void *res;
+
+ offset &= align - 1;
+ if (align < sizeof(void *))
+ align = sizeof(void *);
+
+ res = __crt_aligned_alloc_offset(align, size, offset);
+ if (res == NULL) {
+ rtld_fdputstr(STDERR_FILENO, "Out of memory\n");
+ _exit(1);
+ }
+ return (res);
+}
diff --git a/libexec/rtld-elf32/Makefile b/libexec/rtld-elf32/Makefile
new file mode 100644
index 000000000000..9854223d6f78
--- /dev/null
+++ b/libexec/rtld-elf32/Makefile
@@ -0,0 +1,9 @@
+NEED_COMPAT= 32
+.include <bsd.compat.mk>
+
+PROG= ld-elf32.so.1
+MAN=
+MLINKS= rtld.1 ld-elf32.so.1
+
+.PATH: ${SRCTOP}/libexec/rtld-elf
+.include "${SRCTOP}/libexec/rtld-elf/Makefile"
diff --git a/libexec/save-entropy/Makefile b/libexec/save-entropy/Makefile
new file mode 100644
index 000000000000..5d7e9726b786
--- /dev/null
+++ b/libexec/save-entropy/Makefile
@@ -0,0 +1,4 @@
+SCRIPTS= save-entropy.sh
+MAN= save-entropy.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/save-entropy/Makefile.depend b/libexec/save-entropy/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/save-entropy/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/save-entropy/save-entropy.8 b/libexec/save-entropy/save-entropy.8
new file mode 100644
index 000000000000..f7a93c8866fc
--- /dev/null
+++ b/libexec/save-entropy/save-entropy.8
@@ -0,0 +1,97 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2023 Fernando Apesteguia <fernando.apesteguia@gmail.com>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd September 18, 2023
+.Dt SAVE-ENTROPY 8
+.Os
+.Sh NAME
+.Nm save-entropy
+.Nd Save bits of entropy to feed /dev/random at startup
+.Sh SYNOPSIS
+.Nm save-entropy
+.Sh DESCRIPTION
+The
+.Nm
+command is used to save entropy data from
+.Pa /dev/random
+to files in a specified output location.
+The files saved are used at startup to provide additional entropy for
+.Pa /dev/random .
+The output file will be different in every invocation until the maximum number
+of different files is reached.
+(See
+.Em entropy_save_num
+for details).
+By default this script is invoked via
+.Xr cron 8
+every eleven minutes approximately.
+.Pp
+This command does nothing if executed inside a
+.Xr jail 8 .
+.Pp
+Three variables in
+.Pa /etc/rc.conf
+regulate the behavior of the script:
+.Bl -tag -width Ds
+.It Va entropy_dir
+Specify the directory for saved entropy files.
+Defaults to
+.Pa /var/db/entropy .
+If set to "NO" it disables caching entropy via
+.Xr cron 8 .
+This setting is shared with
+.Pa /etc/rc.d/random .
+.It Va entropy_save_sz
+Size of the entropy cache files.
+Defaults to 4096.
+.It Va entropy_save_num
+Number of entropy cache files to save.
+Defaults to 8.
+.El
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa /etc/rc.conf
+.El
+.Sh EXIT STATUS
+.Ex -std
+Errors will be recorded in the system log.
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr random 4 ,
+.Xr rc.conf 5 ,
+.Xr cron 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+The
+.Nm
+command was originally written by
+.An Doug Barton <dougb@FreeBSD.org> .
+This manual page was written by
+.An Fernando Apesteguia <fernape@FreeBSD.org> .
diff --git a/libexec/save-entropy/save-entropy.sh b/libexec/save-entropy/save-entropy.sh
new file mode 100755
index 000000000000..fc9dbd889f63
--- /dev/null
+++ b/libexec/save-entropy/save-entropy.sh
@@ -0,0 +1,132 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2001-2006,2012 Douglas Barton, dougb@FreeBSD.org
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+# This script is called by cron to store bits of randomness which are
+# then used to seed /dev/random on boot.
+
+# Originally developed by Doug Barton, dougb@FreeBSD.org
+
+PATH=/bin:/usr/bin
+
+# If there is a global system configuration file, suck it in.
+#
+if [ -r /etc/defaults/rc.conf ]; then
+ . /etc/defaults/rc.conf
+ source_rc_confs 2>/dev/null
+elif [ -r /etc/rc.conf ]; then
+ . /etc/rc.conf 2>/dev/null
+fi
+
+[ $(/sbin/sysctl -n security.jail.jailed) = 0 ] || exit 0
+
+case ${entropy_dir} in
+[Nn][Oo])
+ exit 0
+ ;;
+*)
+ entropy_dir=${entropy_dir:-/var/db/entropy}
+ ;;
+esac
+
+entropy_save_sz=${entropy_save_sz:-4096}
+entropy_save_num=${entropy_save_num:-8}
+
+if [ ! -d "${entropy_dir}" ]; then
+ install -d -o operator -g operator -m 0700 "${entropy_dir}" || {
+ logger -is -t "$0" The entropy directory "${entropy_dir}" does \
+ not exist, and cannot be created. Therefore no entropy can \
+ be saved.; exit 1; }
+fi
+
+cd "${entropy_dir}" || {
+ logger -is -t "$0" Cannot cd to the entropy directory: "${entropy_dir}". \
+ Entropy file rotation is aborted.; exit 1; }
+
+for f in saved-entropy.*; do
+ case "${f}" in saved-entropy.\*) continue ;; esac # No files match
+ [ ${f#saved-entropy\.} -gt ${entropy_save_num} ] && unlink ${f}
+done
+
+umask 177
+
+# Scan slots [1..$entropy_save_num), picking an empty slot or the oldest
+# existing file if no empty slot was available.
+#
+# 1. Find out the first regular file or empty slot (and its serial number)
+#
+n=1
+while [ ${n} -le ${entropy_save_num} ]; do
+ save_file="saved-entropy.${n}"
+ if [ ! -e "${save_file}" -o -f "${save_file}" ]; then
+ break
+ else
+ logger -is -t "$0" \
+ "${save_file}" is not a regular file, skipped.
+ fi
+ n=$(( ${n} + 1 ))
+done
+#
+# 2. Start from (serial number + 1), and check if the slot is empty
+# or is an older regular file, update save_file pointer in either
+# case, and break early if we found an empty slot.
+#
+if [ -f ${save_file} ]; then
+ n=$(( ${n} + 1 ))
+ while [ ${n} -le ${entropy_save_num} ]; do
+ next_file=saved-entropy.${n}
+ if [ -f "${next_file}" ]; then
+ [ "${next_file}" -ot "${save_file}" ] && \
+ save_file="${next_file}"
+ elif [ ! -e "${next_file}" ]; then
+ save_file="${next_file}"
+ break
+ else
+ logger -is -t "$0" \
+ "${next_file}" is not a regular file, skipped.
+ fi
+ n=$(( ${n} + 1 ))
+ done
+fi
+#
+# 3. Check if the pointer we have in hand is really a regular file or
+# an empty slot, and bail out as that means there is no available slot.
+#
+if [ -e "${save_file}" -a ! -f "${save_file}" ]; then
+ logger -is -t "$0" \
+ No available slot in "${entropy_dir}", save entropy is aborted.
+ exit 1
+fi
+
+# Save entropy to the selected slot.
+chmod 600 "${save_file}" 2>/dev/null || :
+dd if=/dev/random of="${save_file}" bs=${entropy_save_sz} count=1 2>/dev/null
+chflags nodump "${save_file}" 2>/dev/null || :
+fsync "${save_file}" "."
+
+exit 0
diff --git a/libexec/smrsh/Makefile b/libexec/smrsh/Makefile
new file mode 100644
index 000000000000..345937df2dfe
--- /dev/null
+++ b/libexec/smrsh/Makefile
@@ -0,0 +1,28 @@
+PACKAGE=sendmail
+SENDMAIL_DIR=${SRCTOP}/contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/smrsh
+
+PROG= smrsh
+SRCS= smrsh.c
+MAN= smrsh.8
+CFLAGS+=-I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+
+LIBADD= sm
+
+WARNS?= 2
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h: ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h .NOMETA
+ ln -sf ${.ALLSRC} ${.TARGET}
+
+.include <bsd.prog.mk>
+
+CWARNFLAGS+= ${NO_WDEPRECATED_NON_PROTOTYPE}
diff --git a/libexec/smrsh/Makefile.depend b/libexec/smrsh/Makefile.depend
new file mode 100644
index 000000000000..edde60b1dd6a
--- /dev/null
+++ b/libexec/smrsh/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libsm \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/talkd/Makefile b/libexec/talkd/Makefile
new file mode 100644
index 000000000000..2cdafd5216e0
--- /dev/null
+++ b/libexec/talkd/Makefile
@@ -0,0 +1,7 @@
+PROG= ntalkd
+SRCS= talkd.c announce.c process.c table.c print.c ttymsg.c
+.PATH: ${SRCTOP}/usr.bin/wall
+MAN= talkd.8
+CFLAGS+=-I${SRCTOP}/usr.bin/wall
+
+.include <bsd.prog.mk>
diff --git a/libexec/talkd/Makefile.depend b/libexec/talkd/Makefile.depend
new file mode 100644
index 000000000000..b2cc4ec1ba50
--- /dev/null
+++ b/libexec/talkd/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/protocols \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c
new file mode 100644
index 000000000000..80c663ba48b4
--- /dev/null
+++ b/libexec/talkd/announce.c
@@ -0,0 +1,159 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+
+#include <protocols/talkd.h>
+
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <vis.h>
+
+#include "ttymsg.h"
+#include "extern.h"
+
+/*
+ * Announce an invitation to talk.
+ */
+
+/*
+ * See if the user is accepting messages. If so, announce that
+ * a talk is requested.
+ */
+int
+announce(CTL_MSG *request, const char *remote_machine)
+{
+ char full_tty[32];
+ struct stat stbuf;
+
+ (void)snprintf(full_tty, sizeof(full_tty),
+ "%s%s", _PATH_DEV, request->r_tty);
+ if (stat(full_tty, &stbuf) < 0 || (stbuf.st_mode&020) == 0)
+ return (PERMISSION_DENIED);
+ return (print_mesg(request->r_tty, request, remote_machine));
+}
+
+#define max(a,b) ( (a) > (b) ? (a) : (b) )
+#define N_LINES 5
+#define N_CHARS 256
+
+/*
+ * Build a block of characters containing the message.
+ * It is sent blank filled and in a single block to
+ * try to keep the message in one piece if the recipient
+ * in vi at the time
+ */
+int
+print_mesg(const char *tty, CTL_MSG *request,
+ const char *remote_machine)
+{
+ struct timeval now;
+ time_t clock_sec;
+ struct tm *localclock;
+ struct iovec iovec;
+ char line_buf[N_LINES][N_CHARS];
+ int sizes[N_LINES];
+ char big_buf[N_LINES*N_CHARS];
+ char *bptr, *lptr, *vis_user;
+ int i, j, max_size;
+
+ i = 0;
+ max_size = 0;
+ gettimeofday(&now, NULL);
+ clock_sec = now.tv_sec;
+ localclock = localtime(&clock_sec);
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS,
+ "Message from Talk_Daemon@%s at %d:%02d on %d/%.2d/%.2d ...",
+ hostname, localclock->tm_hour , localclock->tm_min,
+ localclock->tm_year + 1900, localclock->tm_mon + 1,
+ localclock->tm_mday);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+
+ vis_user = malloc(strlen(request->l_name) * 4 + 1);
+ strvis(vis_user, request->l_name, VIS_CSTYLE);
+ (void)snprintf(line_buf[i], N_CHARS,
+ "talk: connection requested by %s@%s", vis_user, remote_machine);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, "talk: respond with: talk %s@%s",
+ vis_user, remote_machine);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ bptr = big_buf;
+ *bptr++ = '\007'; /* send something to wake them up */
+ *bptr++ = '\r'; /* add a \r in case of raw mode */
+ *bptr++ = '\n';
+ for (i = 0; i < N_LINES; i++) {
+ /* copy the line into the big buffer */
+ lptr = line_buf[i];
+ while (*lptr != '\0')
+ *(bptr++) = *(lptr++);
+ /* pad out the rest of the lines with blanks */
+ for (j = sizes[i]; j < max_size + 2; j++)
+ *(bptr++) = ' ';
+ *(bptr++) = '\r'; /* add a \r in case of raw mode */
+ *(bptr++) = '\n';
+ }
+ *bptr = '\0';
+ iovec.iov_base = big_buf;
+ iovec.iov_len = bptr - big_buf;
+ /*
+ * we choose a timeout of RING_WAIT-5 seconds so that we don't
+ * stack up processes trying to write messages to a tty
+ * that is permanently blocked.
+ */
+ if (ttymsg(&iovec, 1, tty, RING_WAIT - 5) != NULL)
+ return (FAILED);
+
+ return (SUCCESS);
+}
diff --git a/libexec/talkd/extern.h b/libexec/talkd/extern.h
new file mode 100644
index 000000000000..60f76e714973
--- /dev/null
+++ b/libexec/talkd/extern.h
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2002 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern int debug;
+extern char hostname[];
+
+int announce(CTL_MSG *, const char *);
+int delete_invite(u_int32_t);
+void do_announce(CTL_MSG *, CTL_RESPONSE *);
+CTL_MSG *find_match(CTL_MSG *request);
+CTL_MSG *find_request(CTL_MSG *request);
+int find_user(const char *name, char *tty);
+void insert_table(CTL_MSG *, CTL_RESPONSE *);
+int new_id(void);
+int print_mesg(const char *, CTL_MSG *, const char *);
+void print_request(const char *, CTL_MSG *);
+void print_response(const char *, CTL_RESPONSE *);
+void process_request(CTL_MSG *mp, CTL_RESPONSE *rp);
+void timeout(int sig);
diff --git a/libexec/talkd/print.c b/libexec/talkd/print.c
new file mode 100644
index 000000000000..8852dceba193
--- /dev/null
+++ b/libexec/talkd/print.c
@@ -0,0 +1,83 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* debug print routines */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <protocols/talkd.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "extern.h"
+
+static const char *types[] =
+ { "leave_invite", "look_up", "delete", "announce" };
+#define NTYPES (sizeof (types) / sizeof (types[0]))
+static const char *answers[] =
+ { "success", "not_here", "failed", "machine_unknown", "permission_denied",
+ "unknown_request", "badversion", "badaddr", "badctladdr" };
+#define NANSWERS (sizeof (answers) / sizeof (answers[0]))
+
+void
+print_request(const char *cp, CTL_MSG *mp)
+{
+ const char *tp;
+ char tbuf[80];
+
+ if (mp->type > NTYPES) {
+ (void)snprintf(tbuf, sizeof(tbuf), "type %d", mp->type);
+ tp = tbuf;
+ } else
+ tp = types[mp->type];
+ syslog(LOG_DEBUG, "%s: %s: id %lu, l_user %s, r_user %s, r_tty %s",
+ cp, tp, (long)mp->id_num, mp->l_name, mp->r_name, mp->r_tty);
+}
+
+void
+print_response(const char *cp, CTL_RESPONSE *rp)
+{
+ const char *tp, *ap;
+ char tbuf[80], abuf[80];
+
+ if (rp->type > NTYPES) {
+ (void)snprintf(tbuf, sizeof(tbuf), "type %d", rp->type);
+ tp = tbuf;
+ } else
+ tp = types[rp->type];
+ if (rp->answer > NANSWERS) {
+ (void)snprintf(abuf, sizeof(abuf), "answer %d", rp->answer);
+ ap = abuf;
+ } else
+ ap = answers[rp->answer];
+ syslog(LOG_DEBUG, "%s: %s: %s, id %d", cp, tp, ap, ntohl(rp->id_num));
+}
diff --git a/libexec/talkd/process.c b/libexec/talkd/process.c
new file mode 100644
index 000000000000..39ef14a37be1
--- /dev/null
+++ b/libexec/talkd/process.c
@@ -0,0 +1,215 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * process.c handles the requests, which can be of three types:
+ * ANNOUNCE - announce to a user that a talk is wanted
+ * LEAVE_INVITE - insert the request into the table
+ * LOOK_UP - look up to see if a request is waiting in
+ * in the table for the local user
+ * DELETE - delete invitation
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <protocols/talkd.h>
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <utmpx.h>
+
+#include "extern.h"
+
+void
+process_request(CTL_MSG *mp, CTL_RESPONSE *rp)
+{
+ CTL_MSG *ptr;
+ char *s;
+
+ rp->vers = TALK_VERSION;
+ rp->type = mp->type;
+ rp->id_num = htonl(0);
+ if (mp->vers != TALK_VERSION) {
+ syslog(LOG_WARNING, "bad protocol version %d", mp->vers);
+ rp->answer = BADVERSION;
+ return;
+ }
+ mp->id_num = ntohl(mp->id_num);
+ mp->addr.sa_family = ntohs(mp->addr.sa_family);
+ if (mp->addr.sa_family != AF_INET) {
+ syslog(LOG_WARNING, "bad address, family %d",
+ mp->addr.sa_family);
+ rp->answer = BADADDR;
+ return;
+ }
+ mp->ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family);
+ if (mp->ctl_addr.sa_family != AF_INET) {
+ syslog(LOG_WARNING, "bad control address, family %d",
+ mp->ctl_addr.sa_family);
+ rp->answer = BADCTLADDR;
+ return;
+ }
+ for (s = mp->l_name; *s; s++)
+ if (!isprint(*s)) {
+ syslog(LOG_NOTICE, "illegal user name. Aborting");
+ rp->answer = FAILED;
+ return;
+ }
+ mp->pid = ntohl(mp->pid);
+ if (debug)
+ print_request("process_request", mp);
+ switch (mp->type) {
+
+ case ANNOUNCE:
+ do_announce(mp, rp);
+ break;
+
+ case LEAVE_INVITE:
+ ptr = find_request(mp);
+ if (ptr != (CTL_MSG *)0) {
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = SUCCESS;
+ } else
+ insert_table(mp, rp);
+ break;
+
+ case LOOK_UP:
+ ptr = find_match(mp);
+ if (ptr != (CTL_MSG *)0) {
+ rp->id_num = htonl(ptr->id_num);
+ rp->addr = ptr->addr;
+ rp->addr.sa_family = htons(ptr->addr.sa_family);
+ rp->answer = SUCCESS;
+ } else
+ rp->answer = NOT_HERE;
+ break;
+
+ case DELETE:
+ rp->answer = delete_invite(mp->id_num);
+ break;
+
+ default:
+ rp->answer = UNKNOWN_REQUEST;
+ break;
+ }
+ if (debug)
+ print_response("process_request", rp);
+}
+
+void
+do_announce(CTL_MSG *mp, CTL_RESPONSE *rp)
+{
+ struct hostent *hp;
+ CTL_MSG *ptr;
+ int result;
+
+ /* see if the user is logged */
+ result = find_user(mp->r_name, mp->r_tty);
+ if (result != SUCCESS) {
+ rp->answer = result;
+ return;
+ }
+#define satosin(sa) ((struct sockaddr_in *)(void *)(sa))
+ hp = gethostbyaddr(&satosin(&mp->ctl_addr)->sin_addr,
+ sizeof (struct in_addr), AF_INET);
+ if (hp == (struct hostent *)0) {
+ rp->answer = MACHINE_UNKNOWN;
+ return;
+ }
+ ptr = find_request(mp);
+ if (ptr == (CTL_MSG *) 0) {
+ insert_table(mp, rp);
+ rp->answer = announce(mp, hp->h_name);
+ return;
+ }
+ if (mp->id_num > ptr->id_num) {
+ /*
+ * This is an explicit re-announce, so update the id_num
+ * field to avoid duplicates and re-announce the talk.
+ */
+ ptr->id_num = new_id();
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = announce(mp, hp->h_name);
+ } else {
+ /* a duplicated request, so ignore it */
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = SUCCESS;
+ }
+}
+
+/*
+ * Search utmp for the local user
+ */
+int
+find_user(const char *name, char *tty)
+{
+ struct utmpx *ut;
+ int status;
+ struct stat statb;
+ time_t best = 0;
+ char ftty[sizeof(_PATH_DEV) - 1 + sizeof(ut->ut_line)];
+
+ setutxent();
+ status = NOT_HERE;
+ (void) strcpy(ftty, _PATH_DEV);
+ while ((ut = getutxent()) != NULL)
+ if (ut->ut_type == USER_PROCESS &&
+ strcmp(ut->ut_user, name) == 0) {
+ if (*tty == '\0' || best != 0) {
+ if (best == 0)
+ status = PERMISSION_DENIED;
+ /* no particular tty was requested */
+ (void) strcpy(ftty + sizeof(_PATH_DEV) - 1,
+ ut->ut_line);
+ if (stat(ftty, &statb) == 0) {
+ if (!(statb.st_mode & 020))
+ continue;
+ if (statb.st_atime > best) {
+ best = statb.st_atime;
+ (void) strcpy(tty, ut->ut_line);
+ status = SUCCESS;
+ continue;
+ }
+ }
+ }
+ if (strcmp(ut->ut_line, tty) == 0) {
+ status = SUCCESS;
+ break;
+ }
+ }
+ endutxent();
+ return (status);
+}
diff --git a/libexec/talkd/table.c b/libexec/talkd/table.c
new file mode 100644
index 000000000000..0de6ff52c667
--- /dev/null
+++ b/libexec/talkd/table.c
@@ -0,0 +1,227 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Routines to handle insertion, deletion, etc on the table
+ * of requests kept by the daemon. Nothing fancy here, linear
+ * search on a double-linked list. A time is kept with each
+ * entry so that overly old invitations can be eliminated.
+ *
+ * Consider this a mis-guided attempt at modularity
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <protocols/talkd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */
+
+#define NIL ((TABLE_ENTRY *)0)
+
+static struct timespec ts;
+
+typedef struct table_entry TABLE_ENTRY;
+
+struct table_entry {
+ CTL_MSG request;
+ long time;
+ TABLE_ENTRY *next;
+ TABLE_ENTRY *last;
+};
+
+static void delete(TABLE_ENTRY *);
+
+static TABLE_ENTRY *table = NIL;
+
+/*
+ * Look in the table for an invitation that matches the current
+ * request looking for an invitation
+ */
+CTL_MSG *
+find_match(CTL_MSG *request)
+{
+ TABLE_ENTRY *ptr, *next;
+ time_t current_time;
+
+ clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
+ current_time = ts.tv_sec;
+ if (debug)
+ print_request("find_match", request);
+ for (ptr = table; ptr != NIL; ptr = next) {
+ next = ptr->next;
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ if (debug)
+ print_request("deleting expired entry",
+ &ptr->request);
+ delete(ptr);
+ continue;
+ }
+ if (debug)
+ print_request("", &ptr->request);
+ if (strcmp(request->l_name, ptr->request.r_name) == 0 &&
+ strcmp(request->r_name, ptr->request.l_name) == 0 &&
+ ptr->request.type == LEAVE_INVITE)
+ return (&ptr->request);
+ }
+ return ((CTL_MSG *)0);
+}
+
+/*
+ * Look for an identical request, as opposed to a complimentary
+ * one as find_match does
+ */
+CTL_MSG *
+find_request(CTL_MSG *request)
+{
+ TABLE_ENTRY *ptr, *next;
+ time_t current_time;
+
+ clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
+ current_time = ts.tv_sec;
+ /*
+ * See if this is a repeated message, and check for
+ * out of date entries in the table while we are it.
+ */
+ if (debug)
+ print_request("find_request", request);
+ for (ptr = table; ptr != NIL; ptr = next) {
+ next = ptr->next;
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ if (debug)
+ print_request("deleting expired entry",
+ &ptr->request);
+ delete(ptr);
+ continue;
+ }
+ if (debug)
+ print_request("", &ptr->request);
+ if (strcmp(request->r_name, ptr->request.r_name) == 0 &&
+ strcmp(request->l_name, ptr->request.l_name) == 0 &&
+ request->type == ptr->request.type &&
+ request->pid == ptr->request.pid) {
+ /* update the time if we 'touch' it */
+ ptr->time = current_time;
+ return (&ptr->request);
+ }
+ }
+ return ((CTL_MSG *)0);
+}
+
+void
+insert_table(CTL_MSG *request, CTL_RESPONSE *response)
+{
+ TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
+ current_time = ts.tv_sec;
+ request->id_num = new_id();
+ response->id_num = htonl(request->id_num);
+ /* insert a new entry into the top of the list */
+ ptr = (TABLE_ENTRY *)malloc(sizeof(TABLE_ENTRY));
+ if (ptr == NIL) {
+ syslog(LOG_ERR, "insert_table: Out of memory");
+ _exit(1);
+ }
+ ptr->time = current_time;
+ ptr->request = *request;
+ ptr->next = table;
+ if (ptr->next != NIL)
+ ptr->next->last = ptr;
+ ptr->last = NIL;
+ table = ptr;
+}
+
+/*
+ * Generate a unique non-zero sequence number
+ */
+int
+new_id(void)
+{
+ static int current_id = 0;
+
+ current_id = (current_id + 1) % MAX_ID;
+ /* 0 is reserved, helps to pick up bugs */
+ if (current_id == 0)
+ current_id = 1;
+ return (current_id);
+}
+
+/*
+ * Delete the invitation with id 'id_num'
+ */
+int
+delete_invite(u_int32_t id_num)
+{
+ TABLE_ENTRY *ptr;
+
+ if (debug)
+ syslog(LOG_DEBUG, "delete_invite(%d)", id_num);
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if (ptr->request.id_num == id_num)
+ break;
+ if (debug)
+ print_request("", &ptr->request);
+ }
+ if (ptr != NIL) {
+ delete(ptr);
+ return (SUCCESS);
+ }
+ return (NOT_HERE);
+}
+
+/*
+ * Classic delete from a double-linked list
+ */
+static void
+delete(TABLE_ENTRY *ptr)
+{
+
+ if (debug)
+ print_request("delete", &ptr->request);
+ if (table == ptr)
+ table = ptr->next;
+ else if (ptr->last != NIL)
+ ptr->last->next = ptr->next;
+ if (ptr->next != NIL)
+ ptr->next->last = ptr->last;
+ free((char *)ptr);
+}
diff --git a/libexec/talkd/talkd.8 b/libexec/talkd/talkd.8
new file mode 100644
index 000000000000..dac7fa5ef0c0
--- /dev/null
+++ b/libexec/talkd/talkd.8
@@ -0,0 +1,73 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd December 11, 1993
+.Dt TALKD 8
+.Os
+.Sh NAME
+.Nm talkd
+.Nd remote user communication server
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is the server that notifies a user that someone else wants to
+initiate a conversation.
+It acts as a repository of invitations, responding to requests
+by clients wishing to rendezvous to hold a conversation.
+In normal operation, a client, the caller,
+initiates a rendezvous by sending a
+.Tn CTL_MSG
+to the server of
+type
+.Tn LOOK_UP
+(see
+.In protocols/talkd.h ) .
+This causes the server to search its invitation
+tables to check if an invitation currently exists for the caller
+(to speak to the callee specified in the message).
+.Pp
+If the lookup fails,
+the caller then sends an
+.Tn ANNOUNCE
+message causing the server to
+broadcast an announcement on the callee's login ports requesting contact.
+.Pp
+When the callee responds, the local server uses the
+recorded invitation to respond with the appropriate rendezvous
+address and the caller and callee client programs establish a
+stream connection through which the conversation takes place.
+.Sh SEE ALSO
+.Xr talk 1 ,
+.Xr write 1
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
diff --git a/libexec/talkd/talkd.c b/libexec/talkd/talkd.c
new file mode 100644
index 000000000000..356985a08a8e
--- /dev/null
+++ b/libexec/talkd/talkd.c
@@ -0,0 +1,125 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The top level of the daemon, the format is heavily borrowed
+ * from rwhod.c. Basically: find out who and where you are;
+ * disconnect all descriptors and ttys, and then endless
+ * loop on waiting for and processing requests
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <protocols/talkd.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static CTL_MSG request;
+static CTL_RESPONSE response;
+
+int debug = 0;
+static long lastmsgtime;
+
+char hostname[MAXHOSTNAMELEN];
+
+#define TIMEOUT 30
+#define MAXIDLE 120
+
+int
+main(int argc, char *argv[])
+{
+ register CTL_MSG *mp = &request;
+ int cc;
+ struct sockaddr ctl_addr;
+
+#ifdef NOTDEF
+ /*
+ * removed so ntalkd can run in tty sandbox
+ */
+ if (getuid())
+ errx(1, "getuid: not super-user");
+#endif
+ openlog("talkd", LOG_PID, LOG_DAEMON);
+ if (gethostname(hostname, sizeof(hostname) - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ _exit(1);
+ }
+ hostname[sizeof(hostname) - 1] = '\0';
+ if (chdir(_PATH_DEV) < 0) {
+ syslog(LOG_ERR, "chdir: %s: %m", _PATH_DEV);
+ _exit(1);
+ }
+ if (argc > 1 && strcmp(argv[1], "-d") == 0)
+ debug = 1;
+ signal(SIGALRM, timeout);
+ alarm(TIMEOUT);
+ for (;;) {
+ cc = recv(0, (char *)mp, sizeof(*mp), 0);
+ if (cc != sizeof (*mp)) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "recv: %m");
+ continue;
+ }
+ lastmsgtime = time(0);
+ (void)memcpy(&ctl_addr.sa_data, &mp->ctl_addr.sa_data,
+ sizeof(ctl_addr.sa_data));
+ ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family);
+ ctl_addr.sa_len = sizeof(ctl_addr);
+ process_request(mp, &response);
+ /* can block here, is this what I want? */
+ cc = sendto(STDIN_FILENO, (char *)&response,
+ sizeof(response), 0, &ctl_addr, sizeof(ctl_addr));
+ if (cc != sizeof (response))
+ syslog(LOG_WARNING, "sendto: %m");
+ }
+}
+
+void
+timeout(int sig __unused)
+{
+ int save_errno = errno;
+
+ if (time(0) - lastmsgtime >= MAXIDLE)
+ _exit(0);
+ alarm(TIMEOUT);
+ errno = save_errno;
+}
diff --git a/libexec/tcpd/Makefile b/libexec/tcpd/Makefile
new file mode 100644
index 000000000000..4845013f7489
--- /dev/null
+++ b/libexec/tcpd/Makefile
@@ -0,0 +1,21 @@
+.include <src.opts.mk>
+
+.PATH: ${SRCTOP}/contrib/tcp_wrappers
+
+PACKAGE= tcpd
+
+PROG= tcpd
+MAN= tcpd.8
+CFLAGS+=-DREAL_DAEMON_DIR=\"${LIBEXECDIR}\" \
+ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 \
+ -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" \
+ -DFACILITY=LOG_DAEMON
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+=-DINET6
+.endif
+
+LIBADD= wrap
+
+WARNS?= 1
+
+.include <bsd.prog.mk>
diff --git a/libexec/tcpd/Makefile.depend b/libexec/tcpd/Makefile.depend
new file mode 100644
index 000000000000..611fd8022de7
--- /dev/null
+++ b/libexec/tcpd/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libwrap \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/tests/Makefile b/libexec/tests/Makefile
new file mode 100644
index 000000000000..29b1b564beca
--- /dev/null
+++ b/libexec/tests/Makefile
@@ -0,0 +1,4 @@
+.PATH: ${SRCTOP}/tests
+KYUAFILE= yes
+
+.include <bsd.test.mk>
diff --git a/libexec/tests/Makefile.depend b/libexec/tests/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/libexec/tests/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/tftp-proxy/Makefile b/libexec/tftp-proxy/Makefile
new file mode 100644
index 000000000000..98b3b3e6ab78
--- /dev/null
+++ b/libexec/tftp-proxy/Makefile
@@ -0,0 +1,13 @@
+.PATH: ${SRCTOP}/contrib/pf/tftp-proxy
+
+PACKAGE= pf
+PROG= tftp-proxy
+SRCS= tftp-proxy.c filter.c
+MAN= tftp-proxy.8
+
+CFLAGS+= -I${SRCTOP}/lib/libpfctl -I${OBJTOP}/lib/libpfctl
+LIBADD= pfctl
+
+WARNS?= 3
+
+.include <bsd.prog.mk>
diff --git a/libexec/tftp-proxy/Makefile.depend b/libexec/tftp-proxy/Makefile.depend
new file mode 100644
index 000000000000..c15d47460507
--- /dev/null
+++ b/libexec/tftp-proxy/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnv \
+ lib/libpfctl \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/tftpd/Makefile b/libexec/tftpd/Makefile
new file mode 100644
index 000000000000..1cea8178ae07
--- /dev/null
+++ b/libexec/tftpd/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PROG= tftpd
+MAN= tftpd.8
+SRCS= tftp-file.c tftp-io.c tftp-options.c tftp-transfer.c tftp-utils.c
+SRCS+= tftpd.c
+
+.if ${MK_TCP_WRAPPERS} != "no"
+CFLAGS+= -DLIBWRAP
+LIBADD= wrap
+.endif
+
+CWARNFLAGS.gcc+= -Wno-format-nonliteral
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
+.include <bsd.prog.mk>
diff --git a/libexec/tftpd/Makefile.depend b/libexec/tftpd/Makefile.depend
new file mode 100644
index 000000000000..344a5d0e9310
--- /dev/null
+++ b/libexec/tftpd/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/tftpd/Makefile.depend.options b/libexec/tftpd/Makefile.depend.options
new file mode 100644
index 000000000000..fbc21670804e
--- /dev/null
+++ b/libexec/tftpd/Makefile.depend.options
@@ -0,0 +1,5 @@
+# This file is not autogenerated - take care!
+
+DIRDEPS_OPTIONS= TCP_WRAPPERS
+
+.include <dirdeps-options.mk>
diff --git a/libexec/tftpd/tests/Makefile b/libexec/tftpd/tests/Makefile
new file mode 100644
index 000000000000..d1faca03331e
--- /dev/null
+++ b/libexec/tftpd/tests/Makefile
@@ -0,0 +1,8 @@
+.include <bsd.own.mk>
+
+ATF_TESTS_C= functional
+TEST_METADATA.functional+= timeout=15
+
+LIBADD= util
+
+.include <bsd.test.mk>
diff --git a/libexec/tftpd/tests/functional.c b/libexec/tftpd/tests/functional.c
new file mode 100644
index 000000000000..791aa9190a2f
--- /dev/null
+++ b/libexec/tftpd/tests/functional.c
@@ -0,0 +1,1283 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 Alan Somers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdalign.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+#include <libutil.h>
+
+static const uint16_t BASEPORT = 6969;
+static const char pidfile[] = "tftpd.pid";
+static int protocol = PF_UNSPEC;
+static int s = -1; /* tftp client socket */
+static struct sockaddr_storage addr; /* Destination address for the client */
+static bool s_flag = false; /* Pass -s to tftpd */
+static bool w_flag = false; /* Pass -w to tftpd */
+
+/* Helper functions*/
+static void require_bufeq(const char *expected, size_t expected_len,
+ const char *actual, size_t len);
+
+/*
+ * Receive a response from tftpd
+ * @param hdr The reply's expected header, as a char array
+ * @param contents The reply's expected contents, as a char array
+ * @param contents_len Length of contents
+ */
+#define RECV(hdr, contents, contents_len) do { \
+ char buffer[1024]; \
+ struct sockaddr_storage from; \
+ socklen_t fromlen = sizeof(from); \
+ ssize_t r = recvfrom(s, buffer, sizeof(buffer), 0, \
+ (struct sockaddr *)&from, &fromlen); \
+ ATF_REQUIRE(r > 0); \
+ require_bufeq((hdr), sizeof(hdr), buffer, \
+ MIN((size_t)r, sizeof(hdr))); \
+ require_bufeq((const char *) (contents), (contents_len), \
+ &buffer[sizeof(hdr)], r - sizeof(hdr)); \
+ if (protocol == PF_INET) { \
+ ((struct sockaddr_in *)&addr)->sin_port = \
+ ((struct sockaddr_in *)&from)->sin_port; \
+ } else { \
+ ((struct sockaddr_in6 *)&addr)->sin6_port = \
+ ((struct sockaddr_in6 *)&from)->sin6_port; \
+ } \
+} while(0)
+
+static void
+recv_ack(uint16_t blocknum)
+{
+ char hdr[] = {0, 4, blocknum >> 8, blocknum & 0xFF};
+ RECV(hdr, NULL, 0);
+}
+
+static void
+recv_oack(const char *options, size_t options_len)
+{
+ char hdr[] = {0, 6};
+ RECV(hdr, options, options_len);
+}
+
+/*
+ * Receive a data packet from tftpd
+ * @param blocknum Expected block number to be received
+ * @param contents Pointer to expected contents
+ * @param contents_len Length of contents expected to receive
+ */
+static void
+recv_data(uint16_t blocknum, const char *contents, size_t contents_len)
+{
+ char hdr[] = {0, 3, blocknum >> 8, blocknum & 0xFF};
+ RECV(hdr, contents, contents_len);
+}
+
+#define RECV_ERROR(code, msg) do { \
+ char hdr[] = {0, 5, code >> 8, code & 0xFF}; \
+ RECV(hdr, msg, sizeof(msg)); \
+} while (0)
+
+/*
+ * send a command to tftpd.
+ * @param cmd Command to send, as a char array
+ */
+static void
+send_bytes(const void *cmd, size_t len)
+{
+ ssize_t r;
+
+ r = sendto(s, cmd, len, 0, (struct sockaddr *)(&addr), addr.ss_len);
+ ATF_REQUIRE(r >= 0);
+ ATF_REQUIRE_EQ(len, (size_t)r);
+}
+
+static void
+send_data(uint16_t blocknum, const char *contents, size_t contents_len)
+{
+ char buffer[1024];
+
+ buffer[0] = 0; /* DATA opcode high byte */
+ buffer[1] = 3; /* DATA opcode low byte */
+ buffer[2] = blocknum >> 8;
+ buffer[3] = blocknum & 0xFF;
+ memmove(&buffer[4], contents, contents_len);
+ send_bytes(buffer, 4 + contents_len);
+}
+
+/*
+ * send a command to tftpd.
+ * @param cmd Command to send, as a const string
+ * (terminating NUL will be ignored)
+ */
+#define SEND_STR(cmd) \
+ ATF_REQUIRE_EQ(sizeof(cmd) - 1, \
+ sendto(s, (cmd), sizeof(cmd) - 1, 0, \
+ (struct sockaddr *)(&addr), addr.ss_len))
+
+/*
+ * Acknowledge block blocknum
+ */
+static void
+send_ack(uint16_t blocknum)
+{
+ char packet[] = {
+ 0, 4, /* ACK opcode in BE */
+ blocknum >> 8,
+ blocknum & 0xFF
+ };
+
+ send_bytes(packet, sizeof(packet));
+}
+
+/*
+ * build an option string
+ */
+#define OPTION_STR(name, value) name "\000" value "\000"
+
+/*
+ * send a read request to tftpd.
+ * @param filename filename as a string, absolute or relative
+ * @param mode either "octet" or "netascii"
+ */
+#define SEND_RRQ(filename, mode) \
+ SEND_STR("\0\001" filename "\0" mode "\0")
+
+/*
+ * send a read request with options
+ */
+#define SEND_RRQ_OPT(filename, mode, options) \
+ SEND_STR("\0\001" filename "\0" mode "\000" options)
+
+/*
+ * send a write request to tftpd.
+ * @param filename filename as a string, absolute or relative
+ * @param mode either "octet" or "netascii"
+ */
+#define SEND_WRQ(filename, mode) \
+ SEND_STR("\0\002" filename "\0" mode "\0")
+
+/*
+ * send a write request with options
+ */
+#define SEND_WRQ_OPT(filename, mode, options) \
+ SEND_STR("\0\002" filename "\0" mode "\000" options)
+
+/* Define a test case, for both IPv4 and IPv6 */
+#define TFTPD_TC_DEFINE(name, head, ...) \
+static void \
+name ## _body(void); \
+ATF_TC_WITH_CLEANUP(name ## _v4); \
+ATF_TC_HEAD(name ## _v4, tc) \
+{ \
+ head \
+} \
+ATF_TC_BODY(name ## _v4, tc) \
+{ \
+ int exitcode = 0; \
+ __VA_ARGS__; \
+ protocol = AF_INET; \
+ s = setup(&addr, __COUNTER__); \
+ name ## _body(); \
+ close(s); \
+ if (exitcode >= 0) \
+ check_server(exitcode); \
+} \
+ATF_TC_CLEANUP(name ## _v4, tc) \
+{ \
+ cleanup(); \
+} \
+ATF_TC_WITH_CLEANUP(name ## _v6); \
+ATF_TC_HEAD(name ## _v6, tc) \
+{ \
+ head \
+} \
+ATF_TC_BODY(name ## _v6, tc) \
+{ \
+ int exitcode = 0; \
+ __VA_ARGS__; \
+ protocol = AF_INET6; \
+ s = setup(&addr, __COUNTER__); \
+ name ## _body(); \
+ close(s); \
+ if (exitcode >= 0) \
+ check_server(exitcode); \
+} \
+ATF_TC_CLEANUP(name ## _v6, tc) \
+{ \
+ cleanup(); \
+} \
+static void \
+name ## _body(void)
+
+/* Add the IPv4 and IPv6 versions of a test case */
+#define TFTPD_TC_ADD(tp, name) do { \
+ ATF_TP_ADD_TC(tp, name ## _v4); \
+ ATF_TP_ADD_TC(tp, name ## _v6); \
+} while (0)
+
+static void
+sigalrm(int signo __unused)
+{
+}
+
+/* Check that server exits with specific exit code */
+static void
+check_server(int exitcode)
+{
+ struct sigaction sa = { .sa_handler = sigalrm };
+ struct itimerval it = { .it_value = { .tv_sec = 30 } };
+ FILE *f;
+ pid_t pid;
+ int wstatus;
+
+ f = fopen(pidfile, "r");
+ ATF_REQUIRE(f != NULL);
+ ATF_REQUIRE_INTEQ(1, fscanf(f, "%d", &pid));
+ ATF_CHECK_INTEQ(0, fclose(f));
+ ATF_REQUIRE_INTEQ(0, sigaction(SIGALRM, &sa, NULL));
+ ATF_REQUIRE_EQ(0, setitimer(ITIMER_REAL, &it, NULL));
+ ATF_REQUIRE_EQ(pid, waitpid(pid, &wstatus, 0));
+ ATF_CHECK(WIFEXITED(wstatus));
+ ATF_CHECK_INTEQ(exitcode, WEXITSTATUS(wstatus));
+ unlink(pidfile);
+}
+
+/* Standard cleanup used by all testcases */
+static void
+cleanup(void)
+{
+ FILE *f;
+ pid_t pid;
+
+ f = fopen(pidfile, "r");
+ if (f == NULL)
+ return;
+ unlink(pidfile);
+ if (fscanf(f, "%d", &pid) == 1) {
+ kill(pid, SIGTERM);
+ waitpid(pid, NULL, 0);
+ }
+ fclose(f);
+}
+
+/* Assert that two binary buffers are identical */
+static void
+require_bufeq(const char *expected, size_t expected_len,
+ const char *actual, size_t len)
+{
+ size_t i;
+
+ ATF_REQUIRE_EQ_MSG(expected_len, len,
+ "Expected %zu bytes but got %zu", expected_len, len);
+ for (i = 0; i < len; i++) {
+ ATF_REQUIRE_EQ_MSG(expected[i], actual[i],
+ "Expected %#hhx at position %zu; got %hhx instead",
+ expected[i], i, actual[i]);
+ }
+}
+
+/*
+ * Start tftpd and return its communicating socket
+ * @param to Will be filled in for use with sendto
+ * @param idx Unique identifier of the test case
+ * @return Socket ready to use
+ */
+static int
+setup(struct sockaddr_storage *to, uint16_t idx)
+{
+ int client_s, server_s, pid, argv_idx;
+ char execname[] = "/usr/libexec/tftpd";
+ char b_flag_str[] = "-b";
+ char s_flag_str[] = "-s";
+ char w_flag_str[] = "-w";
+ char pwd[MAXPATHLEN];
+ char *argv[10];
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ struct sockaddr *server_addr;
+ struct pidfh *pfh;
+ uint16_t port = BASEPORT + idx;
+ socklen_t len;
+ int pd[2];
+
+ ATF_REQUIRE_EQ(0, pipe2(pd, O_CLOEXEC));
+
+ if (protocol == PF_INET) {
+ len = sizeof(addr4);
+ bzero(&addr4, len);
+ addr4.sin_len = len;
+ addr4.sin_family = PF_INET;
+ addr4.sin_port = htons(port);
+ server_addr = (struct sockaddr *)&addr4;
+ } else {
+ len = sizeof(addr6);
+ bzero(&addr6, len);
+ addr6.sin6_len = len;
+ addr6.sin6_family = PF_INET6;
+ addr6.sin6_port = htons(port);
+ server_addr = (struct sockaddr *)&addr6;
+ }
+
+ ATF_REQUIRE_EQ(pwd, getcwd(pwd, sizeof(pwd)));
+
+ /* Must bind(2) pre-fork so it happens before the client's send(2) */
+ server_s = socket(protocol, SOCK_DGRAM, 0);
+ if (server_s < 0 && errno == EAFNOSUPPORT) {
+ atf_tc_skip("This test requires IPv%d support",
+ protocol == PF_INET ? 4 : 6);
+ }
+ ATF_REQUIRE_MSG(server_s >= 0,
+ "socket failed with error %s", strerror(errno));
+ ATF_REQUIRE_EQ_MSG(0, bind(server_s, server_addr, len),
+ "bind failed with error %s", strerror(errno));
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ atf_tc_fail("fork failed");
+ break;
+ case 0:
+ /* In child */
+ pfh = pidfile_open(pidfile, 0644, NULL);
+ ATF_REQUIRE_MSG(pfh != NULL,
+ "pidfile_open: %s", strerror(errno));
+ ATF_REQUIRE_EQ(0, pidfile_write(pfh));
+ ATF_REQUIRE_EQ(0, pidfile_close(pfh));
+
+ bzero(argv, sizeof(argv));
+ argv[0] = execname;
+ argv_idx = 1;
+ argv[argv_idx++] = b_flag_str;
+ if (w_flag)
+ argv[argv_idx++] = w_flag_str;
+ if (s_flag)
+ argv[argv_idx++] = s_flag_str;
+ argv[argv_idx++] = pwd;
+ ATF_REQUIRE_EQ(STDOUT_FILENO, dup2(server_s, STDOUT_FILENO));
+ ATF_REQUIRE_EQ(STDIN_FILENO, dup2(server_s, STDIN_FILENO));
+ ATF_REQUIRE_EQ(STDERR_FILENO, dup2(server_s, STDERR_FILENO));
+ execv(execname, argv);
+ atf_tc_fail("exec failed");
+ break;
+ default:
+ /* In parent */
+ ATF_REQUIRE_INTEQ(0, close(pd[1]));
+ /* block until other end is closed on exec() or exit() */
+ ATF_REQUIRE_INTEQ(0, read(pd[0], &pd[1], sizeof(pd[1])));
+ ATF_REQUIRE_INTEQ(0, close(pd[0]));
+ bzero(to, sizeof(*to));
+ if (protocol == PF_INET) {
+ struct sockaddr_in *to4 = (struct sockaddr_in *)to;
+ to4->sin_len = sizeof(*to4);
+ to4->sin_family = PF_INET;
+ to4->sin_port = htons(port);
+ to4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ } else {
+ struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT;
+ struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)to;
+ to6->sin6_len = sizeof(*to6);
+ to6->sin6_family = PF_INET6;
+ to6->sin6_port = htons(port);
+ to6->sin6_addr = loopback;
+ }
+
+ ATF_REQUIRE_INTEQ(0, close(server_s));
+ ATF_REQUIRE((client_s = socket(protocol, SOCK_DGRAM, 0)) > 0);
+ break;
+ }
+
+ /* Clear the client's umask. Test cases will specify exact modes */
+ umask(0000);
+
+ return (client_s);
+}
+
+/* Like write(2), but never returns less than the requested length */
+static void
+write_all(int fd, const void *buf, size_t nbytes)
+{
+ ssize_t r;
+
+ while (nbytes > 0) {
+ r = write(fd, buf, nbytes);
+ ATF_REQUIRE(r > 0);
+ nbytes -= (size_t)r;
+ buf = (const char *)buf + (size_t)r;
+ }
+}
+
+
+/*
+ * Test Cases
+ */
+
+/*
+ * Read a file, specified by absolute pathname.
+ */
+TFTPD_TC_DEFINE(abspath,)
+{
+ int fd;
+ char command[1024];
+ size_t pathlen;
+ char suffix[] = {'\0', 'o', 'c', 't', 'e', 't', '\0'};
+
+ command[0] = 0; /* RRQ high byte */
+ command[1] = 1; /* RRQ low byte */
+ ATF_REQUIRE(getcwd(&command[2], sizeof(command) - 2) != NULL);
+ pathlen = strlcat(&command[2], "/abspath.txt", sizeof(command) - 2);
+ ATF_REQUIRE(pathlen + sizeof(suffix) < sizeof(command) - 2);
+ memmove(&command[2 + pathlen], suffix, sizeof(suffix));
+
+ fd = open("abspath.txt", O_CREAT | O_RDONLY, 0644);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ send_bytes(command, 2 + pathlen + sizeof(suffix));
+ recv_data(1, NULL, 0);
+ send_ack(1);
+}
+
+/*
+ * Attempt to read a file outside of the allowed directory(ies)
+ */
+TFTPD_TC_DEFINE(dotdot,)
+{
+ ATF_REQUIRE_EQ(0, mkdir("subdir", 0777));
+ SEND_RRQ("../disallowed.txt", "octet");
+ RECV_ERROR(2, "Access violation");
+ s = setup(&addr, __COUNTER__);
+ SEND_RRQ("subdir/../../disallowed.txt", "octet");
+ RECV_ERROR(2, "Access violation");
+ s = setup(&addr, __COUNTER__);
+ SEND_RRQ("/etc/passwd", "octet");
+ RECV_ERROR(2, "Access violation");
+}
+
+/*
+ * With "-s", tftpd should chroot to the specified directory
+ */
+TFTPD_TC_DEFINE(s_flag,
+ atf_tc_set_md_var(tc, "require.user", "root");,
+ s_flag = true)
+{
+ int fd;
+ char contents[] = "small";
+
+ fd = open("small.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, strlen(contents) + 1);
+ close(fd);
+
+ SEND_RRQ("/small.txt", "octet");
+ recv_data(1, contents, strlen(contents) + 1);
+ send_ack(1);
+}
+
+/*
+ * Read a file, and simulate a dropped ACK packet
+ */
+TFTPD_TC_DEFINE(rrq_dropped_ack,)
+{
+ int fd;
+ char contents[] = "small";
+
+ fd = open("small.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, strlen(contents) + 1);
+ close(fd);
+
+ SEND_RRQ("small.txt", "octet");
+ recv_data(1, contents, strlen(contents) + 1);
+ /*
+ * client "sends" the ack, but network drops it
+ * Eventually, tftpd should resend the data packet
+ */
+ recv_data(1, contents, strlen(contents) + 1);
+ send_ack(1);
+}
+
+/*
+ * Read a file, and simulate a dropped DATA packet
+ */
+TFTPD_TC_DEFINE(rrq_dropped_data,)
+{
+ int fd;
+ size_t i;
+ uint32_t contents[192];
+ char buffer[1024];
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, sizeof(contents));
+ close(fd);
+
+ SEND_RRQ("medium.txt", "octet");
+ recv_data(1, (const char *)&contents[0], 512);
+ send_ack(1);
+ (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
+ /*
+ * server "sends" the data, but network drops it
+ * Eventually, client should resend the last ACK
+ */
+ send_ack(1);
+ recv_data(2, (const char *)&contents[128], 256);
+ send_ack(2);
+}
+
+/*
+ * Read a medium file, and simulate a duplicated ACK packet
+ */
+TFTPD_TC_DEFINE(rrq_duped_ack,)
+{
+ int fd;
+ size_t i;
+ uint32_t contents[192];
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, sizeof(contents));
+ close(fd);
+
+ SEND_RRQ("medium.txt", "octet");
+ recv_data(1, (const char *)&contents[0], 512);
+ send_ack(1);
+ send_ack(1); /* Dupe an ACK packet */
+ recv_data(2, (const char *)&contents[128], 256);
+ recv_data(2, (const char *)&contents[128], 256);
+ send_ack(2);
+}
+
+
+/*
+ * Attempt to read a file without read permissions
+ */
+TFTPD_TC_DEFINE(rrq_eaccess,)
+{
+ int fd;
+
+ fd = open("empty.txt", O_CREAT | O_RDONLY, 0000);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_RRQ("empty.txt", "octet");
+ RECV_ERROR(2, "Access violation");
+}
+
+/*
+ * Read an empty file
+ */
+TFTPD_TC_DEFINE(rrq_empty,)
+{
+ int fd;
+
+ fd = open("empty.txt", O_CREAT | O_RDONLY, 0644);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_RRQ("empty.txt", "octet");
+ recv_data(1, NULL, 0);
+ send_ack(1);
+}
+
+/*
+ * Read a medium file of more than one block
+ */
+TFTPD_TC_DEFINE(rrq_medium,)
+{
+ int fd;
+ size_t i;
+ uint32_t contents[192];
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, sizeof(contents));
+ close(fd);
+
+ SEND_RRQ("medium.txt", "octet");
+ recv_data(1, (const char *)&contents[0], 512);
+ send_ack(1);
+ recv_data(2, (const char *)&contents[128], 256);
+ send_ack(2);
+}
+
+/*
+ * Read a medium file with a window size of 2.
+ */
+TFTPD_TC_DEFINE(rrq_medium_window,)
+{
+ int fd;
+ size_t i;
+ uint32_t contents[192];
+ char options[] = OPTION_STR("windowsize", "2");
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, sizeof(contents));
+ close(fd);
+
+ SEND_RRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
+ recv_oack(options, sizeof(options) - 1);
+ send_ack(0);
+ recv_data(1, (const char *)&contents[0], 512);
+ recv_data(2, (const char *)&contents[128], 256);
+ send_ack(2);
+}
+
+/*
+ * Read a file in netascii format
+ */
+TFTPD_TC_DEFINE(rrq_netascii,)
+{
+ int fd;
+ char contents[] = "foo\nbar\rbaz\n";
+ /*
+ * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
+ * is not intended
+ */
+ char expected[] = "foo\r\nbar\r\0baz\r\n";
+
+ fd = open("unix.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, strlen(contents) + 1);
+ close(fd);
+
+ SEND_RRQ("unix.txt", "netascii");
+ recv_data(1, expected, sizeof(expected));
+ send_ack(1);
+}
+
+/*
+ * Read a file that doesn't exist
+ */
+TFTPD_TC_DEFINE(rrq_nonexistent,)
+{
+ SEND_RRQ("nonexistent.txt", "octet");
+ RECV_ERROR(1, "File not found");
+}
+
+/*
+ * Attempt to read a file whose name exceeds PATH_MAX
+ */
+TFTPD_TC_DEFINE(rrq_path_max,)
+{
+#define AReallyBigFileName \
+ "AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
+ ".txt"
+ ATF_REQUIRE_MSG(strlen(AReallyBigFileName) > PATH_MAX,
+ "Somebody increased PATH_MAX. Update the test");
+ SEND_RRQ(AReallyBigFileName, "octet");
+ RECV_ERROR(4, "Illegal TFTP operation");
+}
+
+/*
+ * Read a small file of less than one block
+ */
+TFTPD_TC_DEFINE(rrq_small,)
+{
+ int fd;
+ char contents[] = "small";
+
+ fd = open("small.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, strlen(contents) + 1);
+ close(fd);
+
+ SEND_RRQ("small.txt", "octet");
+ recv_data(1, contents, strlen(contents) + 1);
+ send_ack(1);
+}
+
+/*
+ * Read a file following the example in RFC 7440.
+ */
+TFTPD_TC_DEFINE(rrq_window_rfc7440,)
+{
+ int fd;
+ size_t i;
+ char options[] = OPTION_STR("windowsize", "4");
+ alignas(uint32_t) char contents[13 * 512 - 4];
+ uint32_t *u32p;
+
+ u32p = (uint32_t *)contents;
+ for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
+ u32p[i] = i;
+
+ fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, sizeof(contents));
+ close(fd);
+
+ SEND_RRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
+ recv_oack(options, sizeof(options) - 1);
+ send_ack(0);
+ recv_data(1, &contents[0 * 512], 512);
+ recv_data(2, &contents[1 * 512], 512);
+ recv_data(3, &contents[2 * 512], 512);
+ recv_data(4, &contents[3 * 512], 512);
+ send_ack(4);
+ recv_data(5, &contents[4 * 512], 512);
+ recv_data(6, &contents[5 * 512], 512);
+ recv_data(7, &contents[6 * 512], 512);
+ recv_data(8, &contents[7 * 512], 512);
+
+ /* ACK 5 as if 6-8 were dropped. */
+ send_ack(5);
+ recv_data(6, &contents[5 * 512], 512);
+ recv_data(7, &contents[6 * 512], 512);
+ recv_data(8, &contents[7 * 512], 512);
+ recv_data(9, &contents[8 * 512], 512);
+ send_ack(9);
+ recv_data(10, &contents[9 * 512], 512);
+ recv_data(11, &contents[10 * 512], 512);
+ recv_data(12, &contents[11 * 512], 512);
+ recv_data(13, &contents[12 * 512], 508);
+
+ /* Drop ACK and after timeout receive 10-13. */
+ recv_data(10, &contents[9 * 512], 512);
+ recv_data(11, &contents[10 * 512], 512);
+ recv_data(12, &contents[11 * 512], 512);
+ recv_data(13, &contents[12 * 512], 508);
+ send_ack(13);
+}
+
+/*
+ * Try to transfer a file with an unknown mode.
+ */
+TFTPD_TC_DEFINE(unknown_modes,)
+{
+ SEND_RRQ("foo.txt", "ascii"); /* Misspelling of "ascii" */
+ RECV_ERROR(4, "Illegal TFTP operation");
+ s = setup(&addr, __COUNTER__);
+ SEND_RRQ("foo.txt", "binary"); /* Obsolete. Use "octet" instead */
+ RECV_ERROR(4, "Illegal TFTP operation");
+ s = setup(&addr, __COUNTER__);
+ SEND_RRQ("foo.txt", "en_US.UTF-8");
+ RECV_ERROR(4, "Illegal TFTP operation");
+ s = setup(&addr, __COUNTER__);
+ SEND_RRQ("foo.txt", "mail"); /* Obsolete in RFC-1350 */
+ RECV_ERROR(4, "Illegal TFTP operation");
+}
+
+/*
+ * Send an unknown opcode. tftpd should respond with the appropriate error
+ */
+TFTPD_TC_DEFINE(unknown_opcode,)
+{
+ /* Looks like an RRQ or WRQ request, but with a bad opcode */
+ SEND_STR("\0\007foo.txt\0octet\0");
+ RECV_ERROR(4, "Illegal TFTP operation");
+}
+
+/*
+ * Invoke tftpd with "-w" and write to a nonexistent file.
+ */
+TFTPD_TC_DEFINE(w_flag,, w_flag = 1;)
+{
+ int fd;
+ ssize_t r;
+ char contents[] = "small";
+ char buffer[1024];
+ size_t contents_len;
+
+ contents_len = strlen(contents) + 1;
+ SEND_WRQ("small.txt", "octet");
+ recv_ack(0);
+ send_data(1, contents, contents_len);
+ recv_ack(1);
+
+ fd = open("small.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ ATF_REQUIRE(r > 0);
+ close(fd);
+ require_bufeq(contents, contents_len, buffer, (size_t)r);
+}
+
+/*
+ * Write a medium file, and simulate a dropped ACK packet
+ */
+TFTPD_TC_DEFINE(wrq_dropped_ack,)
+{
+ int fd;
+ size_t i;
+ ssize_t r;
+ uint32_t contents[192];
+ char buffer[1024];
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_WRQ("medium.txt", "octet");
+ recv_ack(0);
+ send_data(1, (const char *)&contents[0], 512);
+ /*
+ * Servers "sends" an ACK packet, but network drops it.
+ * Eventually, server should resend the last ACK
+ */
+ (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
+ recv_ack(1);
+ send_data(2, (const char *)&contents[128], 256);
+ recv_ack(2);
+
+ fd = open("medium.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ ATF_REQUIRE(r > 0);
+ close(fd);
+ require_bufeq((const char *)contents, 768, buffer, (size_t)r);
+}
+
+/*
+ * Write a small file, and simulate a dropped DATA packet
+ */
+TFTPD_TC_DEFINE(wrq_dropped_data,)
+{
+ int fd;
+ ssize_t r;
+ char contents[] = "small";
+ size_t contents_len;
+ char buffer[1024];
+
+ fd = open("small.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+ contents_len = strlen(contents) + 1;
+
+ SEND_WRQ("small.txt", "octet");
+ recv_ack(0);
+ /*
+ * Client "sends" a DATA packet, but network drops it.
+ * Eventually, server should resend the last ACK
+ */
+ recv_ack(0);
+ send_data(1, contents, contents_len);
+ recv_ack(1);
+
+ fd = open("small.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ ATF_REQUIRE(r > 0);
+ close(fd);
+ require_bufeq(contents, contents_len, buffer, (size_t)r);
+}
+
+/*
+ * Write a medium file, and simulate a duplicated DATA packet
+ */
+TFTPD_TC_DEFINE(wrq_duped_data,)
+{
+ int fd;
+ size_t i;
+ ssize_t r;
+ uint32_t contents[192];
+ char buffer[1024];
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_WRQ("medium.txt", "octet");
+ recv_ack(0);
+ send_data(1, (const char *)&contents[0], 512);
+ send_data(1, (const char *)&contents[0], 512);
+ recv_ack(1);
+ recv_ack(1);
+ send_data(2, (const char *)&contents[128], 256);
+ recv_ack(2);
+
+ fd = open("medium.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ ATF_REQUIRE(r > 0);
+ close(fd);
+ require_bufeq((const char *)contents, 768, buffer, (size_t)r);
+}
+
+/*
+ * Attempt to write a file without write permissions
+ */
+TFTPD_TC_DEFINE(wrq_eaccess,)
+{
+ int fd;
+
+ fd = open("empty.txt", O_CREAT | O_RDONLY, 0440);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_WRQ("empty.txt", "octet");
+ RECV_ERROR(2, "Access violation");
+}
+
+/*
+ * Attempt to write a file without world write permissions, but with world
+ * read permissions
+ */
+TFTPD_TC_DEFINE(wrq_eaccess_world_readable,)
+{
+ int fd;
+
+ fd = open("empty.txt", O_CREAT | O_RDONLY, 0444);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_WRQ("empty.txt", "octet");
+ RECV_ERROR(2, "Access violation");
+}
+
+
+/*
+ * Write a medium file of more than one block
+ */
+TFTPD_TC_DEFINE(wrq_medium,)
+{
+ int fd;
+ size_t i;
+ ssize_t r;
+ uint32_t contents[192];
+ char buffer[1024];
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_WRQ("medium.txt", "octet");
+ recv_ack(0);
+ send_data(1, (const char *)&contents[0], 512);
+ recv_ack(1);
+ send_data(2, (const char *)&contents[128], 256);
+ recv_ack(2);
+
+ fd = open("medium.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ ATF_REQUIRE(r > 0);
+ close(fd);
+ require_bufeq((const char *)contents, 768, buffer, (size_t)r);
+}
+
+/*
+ * Write a medium file with a window size of 2.
+ */
+TFTPD_TC_DEFINE(wrq_medium_window,)
+{
+ int fd;
+ size_t i;
+ ssize_t r;
+ uint32_t contents[192];
+ char buffer[1024];
+ char options[] = OPTION_STR("windowsize", "2");
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_WRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
+ recv_oack(options, sizeof(options) - 1);
+ send_data(1, (const char *)&contents[0], 512);
+ send_data(2, (const char *)&contents[128], 256);
+ recv_ack(2);
+
+ fd = open("medium.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ ATF_REQUIRE(r > 0);
+ close(fd);
+ require_bufeq((const char *)contents, 768, buffer, (size_t)r);
+}
+
+/*
+ * Write a file in netascii format
+ */
+TFTPD_TC_DEFINE(wrq_netascii,)
+{
+ int fd;
+ ssize_t r;
+ /*
+ * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
+ * is not intended
+ */
+ char contents[] = "foo\r\nbar\r\0baz\r\n";
+ char expected[] = "foo\nbar\rbaz\n";
+ size_t contents_len;
+ char buffer[1024];
+
+ fd = open("unix.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+ contents_len = sizeof(contents);
+
+ SEND_WRQ("unix.txt", "netascii");
+ recv_ack(0);
+ send_data(1, contents, contents_len);
+ recv_ack(1);
+
+ fd = open("unix.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ ATF_REQUIRE(r > 0);
+ close(fd);
+ require_bufeq(expected, sizeof(expected), buffer, (size_t)r);
+}
+
+/*
+ * Attempt to write to a nonexistent file. With the default options, this
+ * isn't allowed.
+ */
+TFTPD_TC_DEFINE(wrq_nonexistent,)
+{
+ SEND_WRQ("nonexistent.txt", "octet");
+ RECV_ERROR(1, "File not found");
+}
+
+/*
+ * Write a small file of less than one block
+ */
+TFTPD_TC_DEFINE(wrq_small,)
+{
+ int fd;
+ ssize_t r;
+ char contents[] = "small";
+ size_t contents_len;
+ char buffer[1024];
+
+ fd = open("small.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+ contents_len = strlen(contents) + 1;
+
+ SEND_WRQ("small.txt", "octet");
+ recv_ack(0);
+ send_data(1, contents, contents_len);
+ recv_ack(1);
+
+ fd = open("small.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ ATF_REQUIRE(r > 0);
+ close(fd);
+ require_bufeq(contents, contents_len, buffer, (size_t)r);
+}
+
+/*
+ * Write an empty file over a non-empty one
+ */
+TFTPD_TC_DEFINE(wrq_truncate,)
+{
+ int fd;
+ char contents[] = "small";
+ struct stat sb;
+
+ fd = open("small.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, strlen(contents) + 1);
+ close(fd);
+
+ SEND_WRQ("small.txt", "octet");
+ recv_ack(0);
+ send_data(1, NULL, 0);
+ recv_ack(1);
+
+ ATF_REQUIRE_EQ(0, stat("small.txt", &sb));
+ ATF_REQUIRE_EQ(0, sb.st_size);
+}
+
+/*
+ * Write a file following the example in RFC 7440.
+ */
+TFTPD_TC_DEFINE(wrq_window_rfc7440,)
+{
+ int fd;
+ size_t i;
+ ssize_t r;
+ char options[] = OPTION_STR("windowsize", "4");
+ alignas(uint32_t) char contents[13 * 512 - 4];
+ char buffer[sizeof(contents)];
+ uint32_t *u32p;
+
+ u32p = (uint32_t *)contents;
+ for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
+ u32p[i] = i;
+
+ fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_WRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
+ recv_oack(options, sizeof(options) - 1);
+ send_data(1, &contents[0 * 512], 512);
+ send_data(2, &contents[1 * 512], 512);
+ send_data(3, &contents[2 * 512], 512);
+ send_data(4, &contents[3 * 512], 512);
+ recv_ack(4);
+ send_data(5, &contents[4 * 512], 512);
+
+ /* Drop 6-8. */
+ recv_ack(5);
+ send_data(6, &contents[5 * 512], 512);
+ send_data(7, &contents[6 * 512], 512);
+ send_data(8, &contents[7 * 512], 512);
+ send_data(9, &contents[8 * 512], 512);
+ recv_ack(9);
+
+ /* Drop 11. */
+ send_data(10, &contents[9 * 512], 512);
+ send_data(12, &contents[11 * 512], 512);
+
+ /*
+ * We can't send 13 here as tftpd has probably already seen 12
+ * and sent the ACK of 10 if running locally. While it would
+ * recover by sending another ACK of 10, our state machine
+ * would be out of sync.
+ */
+
+ /* Ignore ACK for 10 and resend 10-13. */
+ recv_ack(10);
+ send_data(10, &contents[9 * 512], 512);
+ send_data(11, &contents[10 * 512], 512);
+ send_data(12, &contents[11 * 512], 512);
+ send_data(13, &contents[12 * 512], 508);
+ recv_ack(13);
+
+ fd = open("rfc7440.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ ATF_REQUIRE(r > 0);
+ close(fd);
+ require_bufeq(contents, sizeof(contents), buffer, (size_t)r);
+}
+
+/*
+ * Send less than four bytes
+ */
+TFTPD_TC_DEFINE(short_packet1, /* no head */, exitcode = 1)
+{
+ SEND_STR("\1");
+}
+TFTPD_TC_DEFINE(short_packet2, /* no head */, exitcode = 1)
+{
+ SEND_STR("\1\2");
+}
+TFTPD_TC_DEFINE(short_packet3, /* no head */, exitcode = 1)
+{
+ SEND_STR("\1\2\3");
+}
+
+
+/*
+ * Main
+ */
+
+ATF_TP_ADD_TCS(tp)
+{
+ TFTPD_TC_ADD(tp, abspath);
+ TFTPD_TC_ADD(tp, dotdot);
+ TFTPD_TC_ADD(tp, s_flag);
+ TFTPD_TC_ADD(tp, rrq_dropped_ack);
+ TFTPD_TC_ADD(tp, rrq_dropped_data);
+ TFTPD_TC_ADD(tp, rrq_duped_ack);
+ TFTPD_TC_ADD(tp, rrq_eaccess);
+ TFTPD_TC_ADD(tp, rrq_empty);
+ TFTPD_TC_ADD(tp, rrq_medium);
+ TFTPD_TC_ADD(tp, rrq_medium_window);
+ TFTPD_TC_ADD(tp, rrq_netascii);
+ TFTPD_TC_ADD(tp, rrq_nonexistent);
+ TFTPD_TC_ADD(tp, rrq_path_max);
+ TFTPD_TC_ADD(tp, rrq_small);
+ TFTPD_TC_ADD(tp, rrq_window_rfc7440);
+ TFTPD_TC_ADD(tp, unknown_modes);
+ TFTPD_TC_ADD(tp, unknown_opcode);
+ TFTPD_TC_ADD(tp, w_flag);
+ TFTPD_TC_ADD(tp, wrq_dropped_ack);
+ TFTPD_TC_ADD(tp, wrq_dropped_data);
+ TFTPD_TC_ADD(tp, wrq_duped_data);
+ TFTPD_TC_ADD(tp, wrq_eaccess);
+ TFTPD_TC_ADD(tp, wrq_eaccess_world_readable);
+ TFTPD_TC_ADD(tp, wrq_medium);
+ TFTPD_TC_ADD(tp, wrq_medium_window);
+ TFTPD_TC_ADD(tp, wrq_netascii);
+ TFTPD_TC_ADD(tp, wrq_nonexistent);
+ TFTPD_TC_ADD(tp, wrq_small);
+ TFTPD_TC_ADD(tp, wrq_truncate);
+ TFTPD_TC_ADD(tp, wrq_window_rfc7440);
+ TFTPD_TC_ADD(tp, short_packet1);
+ TFTPD_TC_ADD(tp, short_packet2);
+ TFTPD_TC_ADD(tp, short_packet3);
+
+ return (atf_no_error());
+}
diff --git a/libexec/tftpd/tftp-file.c b/libexec/tftpd/tftp-file.c
new file mode 100644
index 000000000000..405d1b9018c9
--- /dev/null
+++ b/libexec/tftpd/tftp-file.c
@@ -0,0 +1,299 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "tftp-file.h"
+#include "tftp-utils.h"
+
+static FILE *file;
+static int convert;
+
+static char convbuffer[66000];
+static int gotcr = 0;
+
+static size_t
+convert_from_net(char *buffer, size_t count)
+{
+ size_t i, n;
+
+ /*
+ * Convert all CR/LF to LF and all CR,NUL to CR
+ */
+
+ n = 0;
+ for (i = 0; i < count; i++) {
+
+ if (gotcr == 0) {
+ convbuffer[n++] = buffer[i];
+ gotcr = (buffer[i] == '\r');
+ continue;
+ }
+
+ /* CR, NULL -> CR */
+ if (buffer[i] == '\0') {
+ gotcr = 0;
+ continue;
+ }
+
+ /* CR, LF -> LF */
+ if (buffer[i] == '\n') {
+ if (n == 0) {
+ if (ftell(file) != 0) {
+ int r = fseek(file, -1, SEEK_END);
+ assert(r == 0);
+ convbuffer[n++] = '\n';
+ } else {
+ /* This shouldn't happen */
+ tftp_log(LOG_ERR,
+ "Received LF as first character");
+ abort();
+ }
+ } else
+ convbuffer[n-1] = '\n';
+ gotcr = 0;
+ continue;
+ }
+
+ /* Everything else just accept as is */
+ convbuffer[n++] = buffer[i];
+ gotcr = (buffer[i] == '\r');
+ continue;
+ }
+
+ return fwrite(convbuffer, 1, n, file);
+}
+
+static size_t
+convert_to_net(char *buffer, size_t count, int init)
+{
+ size_t i;
+ static size_t n = 0, in = 0;
+ static int newline = -1;
+
+ if (init) {
+ newline = -1;
+ n = 0;
+ in = 0;
+ return 0 ;
+ }
+
+ /*
+ * Convert all LF to CR,LF and all CR to CR,NUL
+ */
+ i = 0;
+
+ if (newline != -1) {
+ buffer[i++] = newline;
+ newline = -1;
+ }
+
+ while (i < count) {
+ if (n == in) {
+ /* When done we're done */
+ if (feof(file)) break;
+
+ /* Otherwise read another bunch */
+ in = fread(convbuffer, 1, count, file);
+ if (in == 0) break;
+ n = 0;
+ }
+
+ /* CR -> CR,NULL */
+ if (convbuffer[n] == '\r') {
+ buffer[i++] = '\r';
+ buffer[i++] = '\0';
+ n++;
+ continue;
+ }
+
+ /* LF -> CR,LF */
+ if (convbuffer[n] == '\n') {
+ buffer[i++] = '\r';
+ buffer[i++] = '\n';
+ n++;
+ continue;
+ }
+
+ buffer[i++] = convbuffer[n++];
+ }
+
+ if (i > count) {
+ /*
+ * Whoops... that isn't allowed (but it will happen
+ * when there is a CR or LF at the end of the buffer)
+ */
+ newline = buffer[i-1];
+ }
+
+ if (i < count) {
+ /* We are done! */
+ return i;
+ } else
+ return count;
+
+}
+
+int
+write_init(int fd, FILE *f, const char *mode)
+{
+
+ if (f == NULL) {
+ file = fdopen(fd, "w");
+ if (file == NULL) {
+ int en = errno;
+ tftp_log(LOG_ERR, "fdopen() failed: %s",
+ strerror(errno));
+ return en;
+ }
+ } else
+ file = f;
+ convert = !strcmp(mode, "netascii");
+ return 0;
+}
+
+size_t
+write_file(char *buffer, int count)
+{
+
+ if (convert == 0)
+ return fwrite(buffer, 1, count, file);
+
+ return convert_from_net(buffer, count);
+}
+
+int
+write_close(void)
+{
+
+ if (fclose(file) != 0) {
+ tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+off_t
+tell_file(void)
+{
+
+ return ftello(file);
+}
+
+int
+seek_file(off_t offset)
+{
+
+ return fseeko(file, offset, SEEK_SET);
+}
+
+int
+read_init(int fd, FILE *f, const char *mode)
+{
+
+ convert_to_net(NULL, 0, 1);
+ if (f == NULL) {
+ file = fdopen(fd, "r");
+ if (file == NULL) {
+ int en = errno;
+ tftp_log(LOG_ERR, "fdopen() failed: %s",
+ strerror(errno));
+ return en;
+ }
+ } else
+ file = f;
+ convert = !strcmp(mode, "netascii");
+ return 0;
+}
+
+size_t
+read_file(char *buffer, int count)
+{
+
+ if (convert == 0)
+ return fread(buffer, 1, count, file);
+
+ return convert_to_net(buffer, count, 0);
+}
+
+int
+read_close(void)
+{
+
+ if (fclose(file) != 0) {
+ tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+
+/* When an error has occurred, it is possible that the two sides
+ * are out of synch. Ie: that what I think is the other side's
+ * response to packet N is really their response to packet N-1.
+ *
+ * So, to try to prevent that, we flush all the input queued up
+ * for us on the network connection on our host.
+ *
+ * We return the number of packets we flushed (mostly for reporting
+ * when trace is active).
+ */
+
+int
+synchnet(int peer) /* socket to flush */
+{
+ int i, j = 0;
+ char rbuf[MAXPKTSIZE];
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ while (1) {
+ (void) ioctl(peer, FIONREAD, &i);
+ if (i) {
+ j++;
+ fromlen = sizeof from;
+ (void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
+ (struct sockaddr *)&from, &fromlen);
+ } else {
+ return(j);
+ }
+ }
+}
diff --git a/libexec/tftpd/tftp-file.h b/libexec/tftpd/tftp-file.h
new file mode 100644
index 000000000000..c424e5cbc75b
--- /dev/null
+++ b/libexec/tftpd/tftp-file.h
@@ -0,0 +1,39 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int write_init(int fd, FILE *f, const char *mode);
+size_t write_file(char *buffer, int count);
+int write_close(void);
+
+int read_init(int fd, FILE *f, const char *mode);
+size_t read_file(char *buffer, int count);
+int read_close(void);
+
+int seek_file(off_t offset);
+off_t tell_file(void);
+
+int synchnet(int peer);
diff --git a/libexec/tftpd/tftp-io.c b/libexec/tftpd/tftp-io.c
new file mode 100644
index 000000000000..50102e652d2f
--- /dev/null
+++ b/libexec/tftpd/tftp-io.c
@@ -0,0 +1,446 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/tftp.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "tftp-file.h"
+#include "tftp-io.h"
+#include "tftp-utils.h"
+#include "tftp-options.h"
+
+struct sockaddr_storage peer_sock;
+struct sockaddr_storage me_sock;
+
+static int send_packet(int peer, uint16_t block, char *pkt, int size);
+
+static struct errmsg {
+ int e_code;
+ const char *e_msg;
+} errmsgs[] = {
+ { EUNDEF, "Undefined error code" },
+ { ENOTFOUND, "File not found" },
+ { EACCESS, "Access violation" },
+ { ENOSPACE, "Disk full or allocation exceeded" },
+ { EBADOP, "Illegal TFTP operation" },
+ { EBADID, "Unknown transfer ID" },
+ { EEXISTS, "File already exists" },
+ { ENOUSER, "No such user" },
+ { EOPTNEG, "Option negotiation" },
+ { -1, NULL }
+};
+
+#define DROPPACKET(s) \
+ if (packetdroppercentage != 0 && \
+ arc4random()%100 < packetdroppercentage) { \
+ tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \
+ return; \
+ }
+#define DROPPACKETn(s,n) \
+ if (packetdroppercentage != 0 && \
+ arc4random()%100 < packetdroppercentage) { \
+ tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \
+ return (n); \
+ }
+
+const char *
+errtomsg(int error)
+{
+ static char ebuf[40];
+ struct errmsg *pe;
+
+ if (error == 0)
+ return ("success");
+ for (pe = errmsgs; pe->e_code >= 0; pe++)
+ if (pe->e_code == error)
+ return (pe->e_msg);
+ snprintf(ebuf, sizeof(ebuf), "error %d", error);
+ return (ebuf);
+}
+
+static int
+send_packet(int peer, uint16_t block, char *pkt, int size)
+{
+ int i;
+ int t = 1;
+
+ for (i = 0; i < 12 ; i++) {
+ DROPPACKETn("send_packet", 0);
+
+ if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock,
+ peer_sock.ss_len) == size) {
+ if (i)
+ tftp_log(LOG_ERR,
+ "%s block %d, attempt %d successful",
+ packettype(ntohs(((struct tftphdr *)
+ (pkt))->th_opcode)), block, i);
+ return (0);
+ }
+ tftp_log(LOG_ERR,
+ "%s block %d, attempt %d failed (Error %d: %s)",
+ packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
+ block, i, errno, strerror(errno));
+ sleep(t);
+ if (t < 32)
+ t <<= 1;
+ }
+ tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
+ return (1);
+}
+
+/*
+ * Send an ERROR packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+void
+send_error(int peer, int error)
+{
+ struct tftphdr *tp;
+ int length;
+ struct errmsg *pe;
+ char buf[MAXPKTSIZE];
+
+ if (debug & DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending ERROR %d", error);
+
+ DROPPACKET("send_error");
+
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = htons((u_short)ERROR);
+ tp->th_code = htons((u_short)error);
+ for (pe = errmsgs; pe->e_code >= 0; pe++)
+ if (pe->e_code == error)
+ break;
+ if (pe->e_code < 0) {
+ pe->e_msg = strerror(error - 100);
+ tp->th_code = EUNDEF; /* set 'undef' errorcode */
+ }
+ snprintf(tp->th_msg, MAXPKTSIZE - 4, "%s%n", pe->e_msg, &length);
+ length += 5; /* header and terminator */
+
+ if (debug & DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
+
+ if (sendto(peer, buf, length, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
+ tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
+}
+
+/*
+ * Send an WRQ packet (write request).
+ */
+int
+send_wrq(int peer, char *filename, char *mode)
+{
+ int n;
+ struct tftphdr *tp;
+ char *bp;
+ char buf[MAXPKTSIZE];
+ int size;
+
+ if (debug & DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
+ filename, mode
+ );
+
+ DROPPACKETn("send_wrq", 0);
+
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = htons((u_short)WRQ);
+ size = offsetof(struct tftphdr, th_stuff);
+
+ bp = tp->th_stuff;
+ strlcpy(bp, filename, sizeof(buf) - size);
+ bp += strlen(filename);
+ *bp = 0;
+ bp++;
+ size += strlen(filename) + 1;
+
+ strlcpy(bp, mode, sizeof(buf) - size);
+ bp += strlen(mode);
+ *bp = 0;
+ bp++;
+ size += strlen(mode) + 1;
+
+ if (options_rfc_enabled)
+ size += make_options(peer, bp, sizeof(buf) - size);
+
+ n = sendto(peer, buf, size, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len);
+ if (n != size) {
+ tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Send an RRQ packet (write request).
+ */
+int
+send_rrq(int peer, char *filename, char *mode)
+{
+ int n;
+ struct tftphdr *tp;
+ char *bp;
+ char buf[MAXPKTSIZE];
+ int size;
+
+ if (debug & DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
+ filename, mode
+ );
+
+ DROPPACKETn("send_rrq", 0);
+
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = htons((u_short)RRQ);
+ size = offsetof(struct tftphdr, th_stuff);
+
+ bp = tp->th_stuff;
+ strlcpy(bp, filename, sizeof(buf) - size);
+ bp += strlen(filename);
+ *bp = 0;
+ bp++;
+ size += strlen(filename) + 1;
+
+ strlcpy(bp, mode, sizeof(buf) - size);
+ bp += strlen(mode);
+ *bp = 0;
+ bp++;
+ size += strlen(mode) + 1;
+
+ if (options_rfc_enabled) {
+ options_set_request(OPT_TSIZE, "0");
+ size += make_options(peer, bp, sizeof(buf) - size);
+ }
+
+ n = sendto(peer, buf, size, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len);
+ if (n != size) {
+ tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Send an OACK packet (option acknowledgement).
+ */
+int
+send_oack(int peer)
+{
+ struct tftphdr *tp;
+ int size, i, n;
+ char *bp;
+ char buf[MAXPKTSIZE];
+
+ if (debug & DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending OACK");
+
+ DROPPACKETn("send_oack", 0);
+
+ /*
+ * Send back an options acknowledgement (only the ones with
+ * a reply for)
+ */
+ tp = (struct tftphdr *)buf;
+ bp = buf + 2;
+ size = sizeof(buf) - 2;
+ tp->th_opcode = htons((u_short)OACK);
+ for (i = 0; options[i].o_type != NULL; i++) {
+ if (options[i].o_reply != NULL) {
+ n = snprintf(bp, size, "%s%c%s", options[i].o_type,
+ 0, options[i].o_reply);
+ bp += n+1;
+ size -= n+1;
+ if (size < 0) {
+ tftp_log(LOG_ERR, "oack: buffer overflow");
+ exit(1);
+ }
+ }
+ }
+ size = bp - buf;
+
+ if (sendto(peer, buf, size, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
+ tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Send an ACK packet (acknowledgement).
+ */
+int
+send_ack(int fp, uint16_t block)
+{
+ struct tftphdr *tp;
+ int size;
+ char buf[MAXPKTSIZE];
+
+ if (debug & DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
+
+ DROPPACKETn("send_ack", 0);
+
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = htons((u_short)ACK);
+ tp->th_block = htons((u_short)block);
+ size = 4;
+
+ if (sendto(fp, buf, size, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
+ tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Send a DATA packet
+ */
+int
+send_data(int peer, uint16_t block, char *data, int size)
+{
+ char buf[MAXPKTSIZE];
+ struct tftphdr *pkt;
+ int n;
+
+ if (debug & DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
+ block, size);
+
+ DROPPACKETn("send_data", 0);
+
+ pkt = (struct tftphdr *)buf;
+
+ pkt->th_opcode = htons((u_short)DATA);
+ pkt->th_block = htons((u_short)block);
+ memcpy(pkt->th_data, data, size);
+
+ n = send_packet(peer, block, (char *)pkt, size + 4);
+ return (n);
+}
+
+
+/*
+ * Receive a packet
+ *
+ * If timeout is negative, no error will be logged on timeout.
+ */
+int
+receive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
+ int timeout)
+{
+ struct pollfd pfd;
+ struct tftphdr *pkt;
+ struct sockaddr_storage from_local;
+ struct sockaddr_storage *pfrom;
+ socklen_t fromlen;
+ int n;
+
+ if (debug & DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG,
+ "Waiting %d seconds for packet", timeoutpacket);
+
+ pkt = (struct tftphdr *)data;
+
+ pfd.fd = peer;
+ pfd.events = POLLIN;
+ if (poll(&pfd, 1, 1000 * (timeout < 0 ? -timeout : timeout)) < 1) {
+ if (timeout > 0)
+ tftp_log(LOG_ERR, "receive_packet: timeout");
+ return (RP_TIMEOUT);
+ }
+
+ pfrom = (from == NULL) ? &from_local : from;
+ fromlen = sizeof(*pfrom);
+ n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
+
+ DROPPACKETn("receive_packet", RP_TIMEOUT);
+
+ if (n < 0) {
+ /* No idea what could have happened if it isn't a timeout */
+ tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
+ return (RP_RECVFROM);
+ }
+ if (n < 4) {
+ tftp_log(LOG_ERR,
+ "receive_packet: packet too small (%d bytes)", n);
+ return (RP_TOOSMALL);
+ }
+
+ pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
+ if (pkt->th_opcode == DATA ||
+ pkt->th_opcode == ACK)
+ pkt->th_block = ntohs((u_short)pkt->th_block);
+
+ if (pkt->th_opcode == DATA && n > pktsize) {
+ tftp_log(LOG_ERR, "receive_packet: packet too big");
+ return (RP_TOOBIG);
+ }
+
+ if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
+ ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
+ tftp_log(LOG_ERR,
+ "receive_packet: received packet from wrong source");
+ return (RP_WRONGSOURCE);
+ }
+
+ if (pkt->th_opcode == ERROR) {
+ tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
+ "Got ERROR packet: %s", pkt->th_msg);
+ return (RP_ERROR);
+ }
+
+ if (debug & DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
+ n, packettype(pkt->th_opcode));
+
+ return n - 4;
+}
diff --git a/libexec/tftpd/tftp-io.h b/libexec/tftpd/tftp-io.h
new file mode 100644
index 000000000000..1d6bc2bd8b5e
--- /dev/null
+++ b/libexec/tftpd/tftp-io.h
@@ -0,0 +1,46 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define RP_NONE 0
+#define RP_RECVFROM -1
+#define RP_TOOSMALL -2
+#define RP_ERROR -3
+#define RP_WRONGSOURCE -4
+#define RP_TIMEOUT -5
+#define RP_TOOBIG -6
+
+const char *errtomsg(int);
+void send_error(int peer, int);
+int send_wrq(int peer, char *, char *);
+int send_rrq(int peer, char *, char *);
+int send_oack(int peer);
+int send_ack(int peer, unsigned short);
+int send_data(int peer, uint16_t, char *, int);
+int receive_packet(int peer, char *, int, struct sockaddr_storage *, int);
+
+extern struct sockaddr_storage peer_sock;
+extern struct sockaddr_storage me_sock;
diff --git a/libexec/tftpd/tftp-options.c b/libexec/tftpd/tftp-options.c
new file mode 100644
index 000000000000..7a261ac3d7c3
--- /dev/null
+++ b/libexec/tftpd/tftp-options.c
@@ -0,0 +1,477 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "tftp-utils.h"
+#include "tftp-io.h"
+#include "tftp-options.h"
+
+/*
+ * Option handlers
+ */
+
+struct options options[] = {
+ { "tsize", NULL, NULL, NULL /* option_tsize */, 1 },
+ { "timeout", NULL, NULL, option_timeout, 1 },
+ { "blksize", NULL, NULL, option_blksize, 1 },
+ { "blksize2", NULL, NULL, option_blksize2, 0 },
+ { "rollover", NULL, NULL, option_rollover, 0 },
+ { "windowsize", NULL, NULL, option_windowsize, 1 },
+ { NULL, NULL, NULL, NULL, 0 }
+};
+
+/* By default allow them */
+int options_rfc_enabled = 1;
+int options_extra_enabled = 1;
+
+int
+options_set_request(enum opt_enum opt, const char *fmt, ...)
+{
+ va_list ap;
+ char *str;
+ int ret;
+
+ if (fmt == NULL) {
+ str = NULL;
+ } else {
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (ret < 0)
+ return (ret);
+ }
+ if (options[opt].o_request != NULL &&
+ options[opt].o_request != options[opt].o_reply)
+ free(options[opt].o_request);
+ options[opt].o_request = str;
+ return (0);
+}
+
+int
+options_set_reply(enum opt_enum opt, const char *fmt, ...)
+{
+ va_list ap;
+ char *str;
+ int ret;
+
+ if (fmt == NULL) {
+ str = NULL;
+ } else {
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (ret < 0)
+ return (ret);
+ }
+ if (options[opt].o_reply != NULL &&
+ options[opt].o_reply != options[opt].o_request)
+ free(options[opt].o_reply);
+ options[opt].o_reply = str;
+ return (0);
+}
+
+static void
+options_set_reply_equal_request(enum opt_enum opt)
+{
+
+ if (options[opt].o_reply != NULL &&
+ options[opt].o_reply != options[opt].o_request)
+ free(options[opt].o_reply);
+ options[opt].o_reply = options[opt].o_request;
+}
+
+/*
+ * Rules for the option handlers:
+ * - If there is no o_request, there will be no processing.
+ *
+ * For servers
+ * - Logging is done as warnings.
+ * - The handler exit()s if there is a serious problem with the
+ * values submitted in the option.
+ *
+ * For clients
+ * - Logging is done as errors. After all, the server shouldn't
+ * return rubbish.
+ * - The handler returns if there is a serious problem with the
+ * values submitted in the option.
+ * - Sending the EBADOP packets is done by the handler.
+ */
+
+int
+option_tsize(int peer __unused, struct tftphdr *tp __unused, int mode,
+ struct stat *stbuf)
+{
+
+ if (options[OPT_TSIZE].o_request == NULL)
+ return (0);
+
+ if (mode == RRQ)
+ options_set_reply(OPT_TSIZE, "%ju", (uintmax_t)stbuf->st_size);
+ else
+ /* XXX Allows writes of all sizes. */
+ options_set_reply_equal_request(OPT_TSIZE);
+ return (0);
+}
+
+int
+option_timeout(int peer)
+{
+ int to;
+
+ if (options[OPT_TIMEOUT].o_request == NULL)
+ return (0);
+
+ to = atoi(options[OPT_TIMEOUT].o_request);
+ if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) {
+ tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
+ "Received bad value for timeout. "
+ "Should be between %d and %d, received %d",
+ TIMEOUT_MIN, TIMEOUT_MAX, to);
+ send_error(peer, EBADOP);
+ if (acting_as_client)
+ return (1);
+ exit(1);
+ } else {
+ timeoutpacket = to;
+ options_set_reply_equal_request(OPT_TIMEOUT);
+ }
+ settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts);
+
+ if (debug & DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting timeout to '%s'",
+ options[OPT_TIMEOUT].o_reply);
+
+ return (0);
+}
+
+int
+option_rollover(int peer)
+{
+
+ if (options[OPT_ROLLOVER].o_request == NULL)
+ return (0);
+
+ if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0
+ && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) {
+ tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
+ "Bad value for rollover, "
+ "should be either 0 or 1, received '%s', "
+ "ignoring request",
+ options[OPT_ROLLOVER].o_request);
+ if (acting_as_client) {
+ send_error(peer, EBADOP);
+ return (1);
+ }
+ return (0);
+ }
+ options_set_reply_equal_request(OPT_ROLLOVER);
+
+ if (debug & DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting rollover to '%s'",
+ options[OPT_ROLLOVER].o_reply);
+
+ return (0);
+}
+
+int
+option_blksize(int peer)
+{
+ u_long maxdgram;
+ size_t len;
+
+ if (options[OPT_BLKSIZE].o_request == NULL)
+ return (0);
+
+ /* maximum size of an UDP packet according to the system */
+ len = sizeof(maxdgram);
+ if (sysctlbyname("net.inet.udp.maxdgram",
+ &maxdgram, &len, NULL, 0) < 0) {
+ tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
+ return (acting_as_client ? 1 : 0);
+ }
+ maxdgram -= 4; /* leave room for header */
+
+ int size = atoi(options[OPT_BLKSIZE].o_request);
+ if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
+ if (acting_as_client) {
+ tftp_log(LOG_ERR,
+ "Invalid blocksize (%d bytes), aborting",
+ size);
+ send_error(peer, EBADOP);
+ return (1);
+ } else {
+ tftp_log(LOG_WARNING,
+ "Invalid blocksize (%d bytes), ignoring request",
+ size);
+ return (0);
+ }
+ }
+
+ if (size > (int)maxdgram) {
+ if (acting_as_client) {
+ tftp_log(LOG_ERR,
+ "Invalid blocksize (%d bytes), "
+ "net.inet.udp.maxdgram sysctl limits it to "
+ "%ld bytes.\n", size, maxdgram);
+ send_error(peer, EBADOP);
+ return (1);
+ } else {
+ tftp_log(LOG_WARNING,
+ "Invalid blocksize (%d bytes), "
+ "net.inet.udp.maxdgram sysctl limits it to "
+ "%ld bytes.\n", size, maxdgram);
+ size = maxdgram;
+ /* No reason to return */
+ }
+ }
+
+ options_set_reply(OPT_BLKSIZE, "%d", size);
+ segsize = size;
+ pktsize = size + 4;
+ if (debug & DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting blksize to '%s'",
+ options[OPT_BLKSIZE].o_reply);
+
+ return (0);
+}
+
+int
+option_blksize2(int peer __unused)
+{
+ u_long maxdgram;
+ int size, i;
+ size_t len;
+
+ int sizes[] = {
+ 8, 16, 32, 64, 128, 256, 512, 1024,
+ 2048, 4096, 8192, 16384, 32768, 0
+ };
+
+ if (options[OPT_BLKSIZE2].o_request == NULL)
+ return (0);
+
+ /* maximum size of an UDP packet according to the system */
+ len = sizeof(maxdgram);
+ if (sysctlbyname("net.inet.udp.maxdgram",
+ &maxdgram, &len, NULL, 0) < 0) {
+ tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
+ return (acting_as_client ? 1 : 0);
+ }
+
+ size = atoi(options[OPT_BLKSIZE2].o_request);
+ for (i = 0; sizes[i] != 0; i++) {
+ if (size == sizes[i]) break;
+ }
+ if (sizes[i] == 0) {
+ tftp_log(LOG_INFO,
+ "Invalid blocksize2 (%d bytes), ignoring request", size);
+ return (acting_as_client ? 1 : 0);
+ }
+
+ if (size > (int)maxdgram) {
+ for (i = 0; sizes[i+1] != 0; i++) {
+ if ((int)maxdgram < sizes[i+1]) break;
+ }
+ tftp_log(LOG_INFO,
+ "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram "
+ "sysctl limits it to %ld bytes.\n", size, maxdgram);
+ size = sizes[i];
+ /* No need to return */
+ }
+
+ options_set_reply(OPT_BLKSIZE2, "%d", size);
+ segsize = size;
+ pktsize = size + 4;
+ if (debug & DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'",
+ options[OPT_BLKSIZE2].o_reply);
+
+ return (0);
+}
+
+int
+option_windowsize(int peer)
+{
+ int size;
+
+ if (options[OPT_WINDOWSIZE].o_request == NULL)
+ return (0);
+
+ size = atoi(options[OPT_WINDOWSIZE].o_request);
+ if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) {
+ if (acting_as_client) {
+ tftp_log(LOG_ERR,
+ "Invalid windowsize (%d blocks), aborting",
+ size);
+ send_error(peer, EBADOP);
+ return (1);
+ } else {
+ tftp_log(LOG_WARNING,
+ "Invalid windowsize (%d blocks), ignoring request",
+ size);
+ return (0);
+ }
+ }
+
+ /* XXX: Should force a windowsize of 1 for non-seekable files. */
+ options_set_reply(OPT_WINDOWSIZE, "%d", size);
+ windowsize = size;
+
+ if (debug & DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting windowsize to '%s'",
+ options[OPT_WINDOWSIZE].o_reply);
+
+ return (0);
+}
+
+/*
+ * Append the available options to the header
+ */
+uint16_t
+make_options(int peer __unused, char *buffer, uint16_t size) {
+ int i;
+ char *value;
+ const char *option;
+ uint16_t length;
+ uint16_t returnsize = 0;
+
+ if (!options_rfc_enabled) return (0);
+
+ for (i = 0; options[i].o_type != NULL; i++) {
+ if (options[i].rfc == 0 && !options_extra_enabled)
+ continue;
+
+ option = options[i].o_type;
+ if (acting_as_client)
+ value = options[i].o_request;
+ else
+ value = options[i].o_reply;
+ if (value == NULL)
+ continue;
+
+ length = strlen(value) + strlen(option) + 2;
+ if (size <= length) {
+ tftp_log(LOG_ERR,
+ "Running out of option space for "
+ "option '%s' with value '%s': "
+ "needed %d bytes, got %d bytes",
+ option, value, size, length);
+ continue;
+ }
+
+ sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000');
+ size -= length;
+ buffer += length;
+ returnsize += length;
+ }
+
+ return (returnsize);
+}
+
+/*
+ * Parse the received options in the header
+ */
+int
+parse_options(int peer, char *buffer, uint16_t size)
+{
+ int i, options_failed;
+ char *c, *cp, *option, *value;
+
+ if (!options_rfc_enabled) return (0);
+
+ /* Parse the options */
+ cp = buffer;
+ options_failed = 0;
+ while (size > 0) {
+ option = cp;
+ i = get_field(peer, cp, size);
+ cp += i;
+
+ value = cp;
+ i = get_field(peer, cp, size);
+ cp += i;
+
+ /* We are at the end */
+ if (*option == '\0') break;
+
+ if (debug & DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG,
+ "option: '%s' value: '%s'", option, value);
+
+ for (c = option; *c; c++)
+ if (isupper(*c))
+ *c = tolower(*c);
+ for (i = 0; options[i].o_type != NULL; i++) {
+ if (strcmp(option, options[i].o_type) == 0) {
+ if (!acting_as_client)
+ options_set_request(i, "%s", value);
+ if (!options_extra_enabled && !options[i].rfc) {
+ tftp_log(LOG_INFO,
+ "Option '%s' with value '%s' found "
+ "but it is not an RFC option",
+ option, value);
+ continue;
+ }
+ if (options[i].o_handler)
+ options_failed +=
+ (options[i].o_handler)(peer);
+ break;
+ }
+ }
+ if (options[i].o_type == NULL)
+ tftp_log(LOG_WARNING,
+ "Unknown option: '%s'", option);
+
+ size -= strlen(option) + strlen(value) + 2;
+ }
+
+ return (options_failed);
+}
+
+/*
+ * Set some default values in the options
+ */
+void
+init_options(void)
+{
+
+ options_set_request(OPT_ROLLOVER, "0");
+}
diff --git a/libexec/tftpd/tftp-options.h b/libexec/tftpd/tftp-options.h
new file mode 100644
index 000000000000..f1b0a5cfaf32
--- /dev/null
+++ b/libexec/tftpd/tftp-options.h
@@ -0,0 +1,68 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Options
+ */
+
+void init_options(void);
+uint16_t make_options(int peer, char *buffer, uint16_t size);
+int parse_options(int peer, char *buffer, uint16_t size);
+
+/* Call back functions */
+int option_tsize(int peer, struct tftphdr *, int, struct stat *);
+int option_timeout(int peer);
+int option_blksize(int peer);
+int option_blksize2(int peer);
+int option_rollover(int peer);
+int option_windowsize(int peer);
+
+extern int options_extra_enabled;
+extern int options_rfc_enabled;
+
+struct options {
+ const char *o_type;
+ char *o_request;
+ char *o_reply;
+ int (*o_handler)(int peer);
+ int rfc;
+};
+
+extern struct options options[];
+enum opt_enum {
+ OPT_TSIZE = 0,
+ OPT_TIMEOUT,
+ OPT_BLKSIZE,
+ OPT_BLKSIZE2,
+ OPT_ROLLOVER,
+ OPT_WINDOWSIZE,
+};
+
+int options_set_request(enum opt_enum, const char *, ...)
+ __printf0like(2, 3);
+int options_set_reply(enum opt_enum, const char *, ...)
+ __printf0like(2, 3);
diff --git a/libexec/tftpd/tftp-transfer.c b/libexec/tftpd/tftp-transfer.c
new file mode 100644
index 000000000000..ea386f8a3c1a
--- /dev/null
+++ b/libexec/tftpd/tftp-transfer.c
@@ -0,0 +1,446 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "tftp-file.h"
+#include "tftp-io.h"
+#include "tftp-utils.h"
+#include "tftp-options.h"
+#include "tftp-transfer.h"
+
+struct block_data {
+ off_t offset;
+ uint16_t block;
+ int size;
+};
+
+/*
+ * Send a file via the TFTP data session.
+ */
+int
+tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
+{
+ struct tftphdr *rp;
+ int size, n_data, n_ack, sendtry, acktry;
+ u_int i, j;
+ uint16_t oldblock, windowblock;
+ char sendbuffer[MAXPKTSIZE];
+ char recvbuffer[MAXPKTSIZE];
+ struct block_data window[WINDOWSIZE_MAX];
+
+ rp = (struct tftphdr *)recvbuffer;
+ *block = 1;
+ ts->amount = 0;
+ windowblock = 0;
+ acktry = 0;
+ do {
+read_block:
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Sending block %d (window block %d)",
+ *block, windowblock);
+
+ window[windowblock].offset = tell_file();
+ window[windowblock].block = *block;
+ size = read_file(sendbuffer, segsize);
+ if (size < 0) {
+ tftp_log(LOG_ERR, "read_file returned %d", size);
+ send_error(peer, errno + 100);
+ return -1;
+ }
+ window[windowblock].size = size;
+ windowblock++;
+
+ for (sendtry = 0; ; sendtry++) {
+ n_data = send_data(peer, *block, sendbuffer, size);
+ if (n_data == 0)
+ break;
+
+ if (sendtry == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Cannot send DATA packet #%d, "
+ "giving up", *block);
+ return -1;
+ }
+ tftp_log(LOG_ERR,
+ "Cannot send DATA packet #%d, trying again",
+ *block);
+ }
+
+ /* Only check for ACK for last block in window. */
+ if (windowblock == windowsize || size != segsize) {
+ n_ack = receive_packet(peer, recvbuffer,
+ MAXPKTSIZE, NULL, timeoutpacket);
+ if (n_ack < 0) {
+ if (n_ack == RP_TIMEOUT) {
+ if (acktry == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Timeout #%d send ACK %d "
+ "giving up", acktry, *block);
+ return -1;
+ }
+ tftp_log(LOG_WARNING,
+ "Timeout #%d on ACK %d",
+ acktry, *block);
+
+ acktry++;
+ ts->retries++;
+ if (seek_file(window[0].offset) != 0) {
+ tftp_log(LOG_ERR,
+ "seek_file failed: %s",
+ strerror(errno));
+ send_error(peer, errno + 100);
+ return -1;
+ }
+ *block = window[0].block;
+ windowblock = 0;
+ goto read_block;
+ }
+
+ /* Either read failure or ERROR packet */
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_ERR, "Aborting: %s",
+ rp_strerror(n_ack));
+ return -1;
+ }
+ if (rp->th_opcode == ACK) {
+ /*
+ * Look for the ACKed block in our open
+ * window.
+ */
+ for (i = 0; i < windowblock; i++) {
+ if (rp->th_block == window[i].block)
+ break;
+ }
+
+ if (i == windowblock) {
+ /* Did not recognize ACK. */
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "ACK %d out of window",
+ rp->th_block);
+
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+
+ /* Resend the current window. */
+ ts->retries++;
+ if (seek_file(window[0].offset) != 0) {
+ tftp_log(LOG_ERR,
+ "seek_file failed: %s",
+ strerror(errno));
+ send_error(peer, errno + 100);
+ return -1;
+ }
+ *block = window[0].block;
+ windowblock = 0;
+ goto read_block;
+ }
+
+ /* ACKed at least some data. */
+ acktry = 0;
+ for (j = 0; j <= i; j++) {
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "ACKed block %d",
+ window[j].block);
+ ts->blocks++;
+ ts->amount += window[j].size;
+ }
+
+ /*
+ * Partial ACK. Rewind state to first
+ * un-ACKed block.
+ */
+ if (i + 1 != windowblock) {
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "Partial ACK");
+ if (seek_file(window[i + 1].offset) !=
+ 0) {
+ tftp_log(LOG_ERR,
+ "seek_file failed: %s",
+ strerror(errno));
+ send_error(peer, errno + 100);
+ return -1;
+ }
+ *block = window[i + 1].block;
+ windowblock = 0;
+ ts->retries++;
+ goto read_block;
+ }
+
+ windowblock = 0;
+ }
+
+ }
+ oldblock = *block;
+ (*block)++;
+ if (oldblock > *block) {
+ if (options[OPT_ROLLOVER].o_request == NULL) {
+ /*
+ * "rollover" option not specified in
+ * tftp client. Default to rolling block
+ * counter to 0.
+ */
+ *block = 0;
+ } else {
+ *block = atoi(options[OPT_ROLLOVER].o_request);
+ }
+
+ ts->rollovers++;
+ }
+ gettimeofday(&(ts->tstop), NULL);
+ } while (size == segsize);
+ return 0;
+}
+
+/*
+ * Receive a file via the TFTP data session.
+ *
+ * - It could be that the first block has already arrived while
+ * trying to figure out if we were receiving options or not. In
+ * that case it is passed to this function.
+ */
+int
+tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
+ struct tftphdr *firstblock, size_t fb_size)
+{
+ struct tftphdr *rp;
+ uint16_t oldblock, windowstart;
+ int n_data, n_ack, writesize, i, retry, windowblock;
+ char recvbuffer[MAXPKTSIZE];
+
+ ts->amount = 0;
+ windowblock = 0;
+
+ if (firstblock != NULL) {
+ writesize = write_file(firstblock->th_data, fb_size);
+ ts->amount += writesize;
+ ts->blocks++;
+ windowblock++;
+ if (windowsize == 1 || fb_size != segsize) {
+ for (i = 0; ; i++) {
+ n_ack = send_ack(peer, *block);
+ if (n_ack > 0) {
+ if (i == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Cannot send ACK packet #%d, "
+ "giving up", *block);
+ return -1;
+ }
+ tftp_log(LOG_ERR,
+ "Cannot send ACK packet #%d, trying again",
+ *block);
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ if (fb_size != segsize) {
+ write_close();
+ gettimeofday(&(ts->tstop), NULL);
+ return 0;
+ }
+ }
+
+ rp = (struct tftphdr *)recvbuffer;
+ do {
+ oldblock = *block;
+ (*block)++;
+ if (oldblock > *block) {
+ if (options[OPT_ROLLOVER].o_request == NULL) {
+ /*
+ * "rollover" option not specified in
+ * tftp client. Default to rolling block
+ * counter to 0.
+ */
+ *block = 0;
+ } else {
+ *block = atoi(options[OPT_ROLLOVER].o_request);
+ }
+
+ ts->rollovers++;
+ }
+
+ for (retry = 0; ; retry++) {
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "Receiving DATA block %d (window block %d)",
+ *block, windowblock);
+
+ n_data = receive_packet(peer, recvbuffer,
+ MAXPKTSIZE, NULL, timeoutpacket);
+ if (n_data < 0) {
+ if (retry == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Timeout #%d on DATA block %d, "
+ "giving up", retry, *block);
+ return -1;
+ }
+ if (n_data == RP_TIMEOUT) {
+ tftp_log(LOG_WARNING,
+ "Timeout #%d on DATA block %d",
+ retry, *block);
+ send_ack(peer, oldblock);
+ windowblock = 0;
+ continue;
+ }
+
+ /* Either read failure or ERROR packet */
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Aborting: %s",
+ rp_strerror(n_data));
+ return -1;
+ }
+ if (rp->th_opcode == DATA) {
+ ts->blocks++;
+
+ if (rp->th_block == *block)
+ break;
+
+ /*
+ * Ignore duplicate blocks within the
+ * window.
+ *
+ * This does not handle duplicate
+ * blocks during a rollover as
+ * gracefully, but that should still
+ * recover eventually.
+ */
+ if (*block > windowsize)
+ windowstart = *block - windowsize;
+ else
+ windowstart = 0;
+ if (rp->th_block > windowstart &&
+ rp->th_block < *block) {
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "Ignoring duplicate DATA block %d",
+ rp->th_block);
+ windowblock++;
+ retry = 0;
+ continue;
+ }
+
+ tftp_log(LOG_WARNING,
+ "Expected DATA block %d, got block %d",
+ *block, rp->th_block);
+
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+
+ tftp_log(LOG_INFO, "Trying to sync");
+ *block = oldblock;
+ ts->retries++;
+ goto send_ack; /* rexmit */
+
+ } else {
+ tftp_log(LOG_WARNING,
+ "Expected DATA block, got %s block",
+ packettype(rp->th_opcode));
+ }
+ }
+
+ if (n_data > 0) {
+ writesize = write_file(rp->th_data, n_data);
+ ts->amount += writesize;
+ if (writesize <= 0) {
+ tftp_log(LOG_ERR,
+ "write_file returned %d", writesize);
+ if (writesize < 0)
+ send_error(peer, errno + 100);
+ else
+ send_error(peer, ENOSPACE);
+ return -1;
+ }
+ }
+ if (n_data != segsize)
+ write_close();
+ windowblock++;
+
+ /* Only send ACKs for the last block in the window. */
+ if (windowblock < windowsize && n_data == segsize)
+ continue;
+send_ack:
+ for (i = 0; ; i++) {
+ n_ack = send_ack(peer, *block);
+ if (n_ack > 0) {
+
+ if (i == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Cannot send ACK packet #%d, "
+ "giving up", *block);
+ return -1;
+ }
+
+ tftp_log(LOG_ERR,
+ "Cannot send ACK packet #%d, trying again",
+ *block);
+ continue;
+ }
+
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Sent ACK for %d", *block);
+ windowblock = 0;
+ break;
+ }
+ gettimeofday(&(ts->tstop), NULL);
+ } while (n_data == segsize);
+
+ /* Don't do late packet management for the client implementation */
+ if (acting_as_client)
+ return 0;
+
+ for (i = 0; ; i++) {
+ n_data = receive_packet(peer, (char *)rp, pktsize,
+ NULL, -timeoutpacket);
+ if (n_data <= 0)
+ break;
+ if (n_data > 0 &&
+ rp->th_opcode == DATA && /* and got a data block */
+ *block == rp->th_block) /* then my last ack was lost */
+ send_ack(peer, *block); /* resend final ack */
+ }
+
+ return 0;
+}
diff --git a/libexec/tftpd/tftp-transfer.h b/libexec/tftpd/tftp-transfer.h
new file mode 100644
index 000000000000..449f29c246e0
--- /dev/null
+++ b/libexec/tftpd/tftp-transfer.h
@@ -0,0 +1,30 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int tftp_send(int peer, uint16_t *block, struct tftp_stats *tp);
+int tftp_receive(int peer, uint16_t *block, struct tftp_stats *tp,
+ struct tftphdr *firstblock, size_t fb_size);
diff --git a/libexec/tftpd/tftp-utils.c b/libexec/tftpd/tftp-utils.c
new file mode 100644
index 000000000000..8ce7c09c9992
--- /dev/null
+++ b/libexec/tftpd/tftp-utils.c
@@ -0,0 +1,328 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "tftp-utils.h"
+#include "tftp-io.h"
+
+/*
+ * Default values, can be changed later via the TFTP Options
+ */
+int timeoutpacket = TIMEOUT;
+int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT;
+int maxtimeouts = MAX_TIMEOUTS;
+uint16_t segsize = SEGSIZE;
+uint16_t pktsize = SEGSIZE + 4;
+uint16_t windowsize = WINDOWSIZE;
+
+int acting_as_client;
+
+
+/*
+ * Set timeout values for packet reception. The idea is that you
+ * get 'maxtimeouts' of 5 seconds between 'timeoutpacket' (i.e. the
+ * first timeout) to 'timeoutnetwork' (i.e. the last timeout)
+ */
+int
+settimeouts(int _timeoutpacket, int _timeoutnetwork, int _maxtimeouts __unused)
+{
+ int i;
+
+ /* We cannot do impossible things */
+ if (_timeoutpacket >= _timeoutnetwork)
+ return (0);
+
+ maxtimeouts = 0;
+ i = _timeoutpacket;
+ while (i < _timeoutnetwork || maxtimeouts < MIN_TIMEOUTS) {
+ maxtimeouts++;
+ i += 5;
+ }
+
+ timeoutpacket = _timeoutpacket;
+ timeoutnetwork = i;
+ return (1);
+}
+
+/* translate IPv4 mapped IPv6 address to IPv4 address */
+void
+unmappedaddr(struct sockaddr_in6 *sin6)
+{
+ struct sockaddr_in *sin4;
+ u_int32_t addr;
+ int port;
+
+ if (sin6->sin6_family != AF_INET6 ||
+ !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ return;
+ sin4 = (struct sockaddr_in *)sin6;
+ memcpy(&addr, &sin6->sin6_addr.s6_addr[12], sizeof(addr));
+ port = sin6->sin6_port;
+ memset(sin4, 0, sizeof(struct sockaddr_in));
+ sin4->sin_addr.s_addr = addr;
+ sin4->sin_port = port;
+ sin4->sin_family = AF_INET;
+ sin4->sin_len = sizeof(struct sockaddr_in);
+}
+
+/* Get a field from a \0 separated string */
+size_t
+get_field(int peer, char *buffer, size_t size)
+{
+ char *cp = buffer;
+
+ while (cp < buffer + size) {
+ if (*cp == '\0') break;
+ cp++;
+ }
+ if (*cp != '\0') {
+ tftp_log(LOG_ERR, "Bad option - no trailing \\0 found");
+ send_error(peer, EBADOP);
+ exit(1);
+ }
+ return (cp - buffer + 1);
+}
+
+/*
+ * Logging functions
+ */
+static int _tftp_logtostdout = 1;
+
+void
+tftp_openlog(const char *ident, int logopt, int facility)
+{
+
+ _tftp_logtostdout = (ident == NULL);
+ if (_tftp_logtostdout == 0)
+ openlog(ident, logopt, facility);
+}
+
+void
+tftp_closelog(void)
+{
+
+ if (_tftp_logtostdout == 0)
+ closelog();
+}
+
+void
+tftp_log(int priority, const char *message, ...)
+{
+ va_list ap;
+ int serrno;
+ char *s;
+
+ serrno = errno;
+ va_start(ap, message);
+ if (_tftp_logtostdout == 0) {
+ vasprintf(&s, message, ap);
+ syslog(priority, "%s", s);
+ } else {
+ vprintf(message, ap);
+ printf("\n");
+ }
+ va_end(ap);
+ errno = serrno;
+}
+
+/*
+ * Packet types
+ */
+struct packettypes packettypes[] = {
+ { RRQ, "RRQ" },
+ { WRQ, "WRQ" },
+ { DATA, "DATA" },
+ { ACK, "ACK" },
+ { ERROR, "ERROR" },
+ { OACK, "OACK" },
+ { 0, NULL },
+};
+
+const char *
+packettype(int type)
+{
+ static char failed[100];
+ int i = 0;
+
+ while (packettypes[i].name != NULL) {
+ if (packettypes[i].value == type)
+ break;
+ i++;
+ }
+ if (packettypes[i].name != NULL)
+ return packettypes[i].name;
+ sprintf(failed, "unknown (type: %d)", type);
+ return (failed);
+}
+
+/*
+ * Debugs
+ */
+int debug = DEBUG_NONE;
+struct debugs debugs[] = {
+ { DEBUG_PACKETS, "packet", "Packet debugging" },
+ { DEBUG_SIMPLE, "simple", "Simple debugging" },
+ { DEBUG_OPTIONS, "options", "Options debugging" },
+ { DEBUG_ACCESS, "access", "TCPd access debugging" },
+ { DEBUG_NONE, NULL, "No debugging" },
+};
+unsigned int packetdroppercentage = 0;
+
+int
+debug_find(char *s)
+{
+ int i = 0;
+
+ while (debugs[i].name != NULL) {
+ if (strcasecmp(debugs[i].name, s) == 0)
+ break;
+ i++;
+ }
+ return (debugs[i].value);
+}
+
+int
+debug_finds(char *s)
+{
+ int i = 0;
+ char *ps = s;
+
+ while (s != NULL) {
+ ps = strchr(s, ' ');
+ if (ps != NULL)
+ *ps = '\0';
+ i += debug_find(s);
+ if (ps != NULL)
+ *ps = ' ';
+ s = ps;
+ }
+ return (i);
+}
+
+const char *
+debug_show(int d)
+{
+ static char s[100];
+ size_t space = sizeof(s);
+ int i = 0;
+
+ s[0] = '\0';
+ while (debugs[i].name != NULL) {
+ if (d&debugs[i].value) {
+ if (s[0] != '\0')
+ strlcat(s, " ", space);
+ strlcat(s, debugs[i].name, space);
+ }
+ i++;
+ }
+ if (s[0] != '\0')
+ return (s);
+ return ("none");
+}
+
+/*
+ * RP_
+ */
+struct rp_errors rp_errors[] = {
+ { RP_TIMEOUT, "Network timeout" },
+ { RP_TOOSMALL, "Not enough data bytes" },
+ { RP_WRONGSOURCE, "Invalid IP address of UDP port" },
+ { RP_ERROR, "Error packet" },
+ { RP_RECVFROM, "recvfrom() complained" },
+ { RP_TOOBIG, "Too many data bytes" },
+ { RP_NONE, NULL }
+};
+
+char *
+rp_strerror(int error)
+{
+ static char s[100];
+ size_t space = sizeof(s);
+ int i = 0;
+
+ while (rp_errors[i].desc != NULL) {
+ if (rp_errors[i].error == error) {
+ strlcpy(s, rp_errors[i].desc, space);
+ space -= strlen(rp_errors[i].desc);
+ }
+ i++;
+ }
+ if (s[0] == '\0')
+ sprintf(s, "unknown (error=%d)", error);
+ return (s);
+}
+
+/*
+ * Performance figures
+ */
+
+void
+stats_init(struct tftp_stats *ts)
+{
+
+ ts->amount = 0;
+ ts->rollovers = 0;
+ ts->retries = 0;
+ ts->blocks = 0;
+ ts->amount = 0;
+ gettimeofday(&(ts->tstart), NULL);
+}
+
+void
+printstats(const char *direction, int verbose, struct tftp_stats *ts)
+{
+ double delta; /* compute delta in 1/10's second units */
+
+ delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) -
+ ((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000));
+ delta = delta/10.; /* back to seconds */
+
+ printf("%s %zu bytes during %.1f seconds in %u blocks",
+ direction, ts->amount, delta, ts->blocks);
+
+ if (ts->rollovers != 0)
+ printf(" with %d rollover%s",
+ ts->rollovers, ts->rollovers != 1 ? "s" : "");
+
+ if (verbose)
+ printf(" [%.0f bits/sec]", (ts->amount*8.)/delta);
+ putchar('\n');
+}
diff --git a/libexec/tftpd/tftp-utils.h b/libexec/tftpd/tftp-utils.h
new file mode 100644
index 000000000000..276dedcf74cd
--- /dev/null
+++ b/libexec/tftpd/tftp-utils.h
@@ -0,0 +1,129 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ */
+#define TIMEOUT 5
+#define MAX_TIMEOUTS 5
+
+/* Generic values */
+#define MAXSEGSIZE 65464 /* Maximum size of the data segment */
+#define MAXPKTSIZE (MAXSEGSIZE + 4) /* Maximum size of the packet */
+
+/* For the blksize option */
+#define BLKSIZE_MIN 8 /* Minimum size of the data segment */
+#define BLKSIZE_MAX MAXSEGSIZE /* Maximum size of the data segment */
+
+/* For the timeout option */
+#define TIMEOUT_MIN 0 /* Minimum timeout value */
+#define TIMEOUT_MAX 255 /* Maximum timeout value */
+#define MIN_TIMEOUTS 3
+
+/* For the windowsize option */
+#define WINDOWSIZE 1
+#define WINDOWSIZE_MIN 1
+#define WINDOWSIZE_MAX 65535
+
+extern int timeoutpacket;
+extern int timeoutnetwork;
+extern int maxtimeouts;
+int settimeouts(int timeoutpacket, int timeoutnetwork, int maxtimeouts);
+
+extern uint16_t segsize;
+extern uint16_t pktsize;
+extern uint16_t windowsize;
+
+extern int acting_as_client;
+
+/*
+ */
+void unmappedaddr(struct sockaddr_in6 *sin6);
+size_t get_field(int peer, char *buffer, size_t size);
+
+/*
+ * Packet types
+ */
+struct packettypes {
+ int value;
+ const char *const name;
+};
+extern struct packettypes packettypes[];
+const char *packettype(int);
+
+/*
+ * RP_
+ */
+struct rp_errors {
+ int error;
+ const char *const desc;
+};
+extern struct rp_errors rp_errors[];
+char *rp_strerror(int error);
+
+/*
+ * Debug features
+ */
+#define DEBUG_NONE 0x0000
+#define DEBUG_PACKETS 0x0001
+#define DEBUG_SIMPLE 0x0002
+#define DEBUG_OPTIONS 0x0004
+#define DEBUG_ACCESS 0x0008
+struct debugs {
+ int value;
+ const char *const name;
+ const char *const desc;
+};
+extern int debug;
+extern struct debugs debugs[];
+extern unsigned int packetdroppercentage;
+int debug_find(char *s);
+int debug_finds(char *s);
+const char *debug_show(int d);
+
+/*
+ * Log routines
+ */
+#define DEBUG(s) tftp_log(LOG_DEBUG, "%s", s)
+extern int tftp_logtostdout;
+void tftp_openlog(const char *ident, int logopt, int facility);
+void tftp_closelog(void);
+void tftp_log(int priority, const char *message, ...) __printflike(2, 3);
+
+/*
+ * Performance figures
+ */
+struct tftp_stats {
+ size_t amount;
+ int rollovers;
+ uint32_t blocks;
+ int retries;
+ struct timeval tstart;
+ struct timeval tstop;
+};
+
+void stats_init(struct tftp_stats *ts);
+void printstats(const char *direction, int verbose, struct tftp_stats *ts);
diff --git a/libexec/tftpd/tftpd.8 b/libexec/tftpd/tftpd.8
new file mode 100644
index 000000000000..0118198da53d
--- /dev/null
+++ b/libexec/tftpd/tftpd.8
@@ -0,0 +1,333 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd November 3, 2024
+.Dt TFTPD 8
+.Os
+.Sh NAME
+.Nm tftpd
+.Nd Internet Trivial File Transfer Protocol server
+.Sh SYNOPSIS
+.Nm tftpd
+.Op Fl bCcdlnoSw
+.Op Fl F Ar strftime-format
+.Op Fl s Ar directory
+.Op Fl U Ar umask
+.Op Fl u Ar user
+.Op Ar directory ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is a server which supports the
+Internet Trivial File Transfer
+Protocol
+.Pq Tn RFC 1350 .
+The
+.Tn TFTP
+server operates
+at the port indicated in the
+.Ql tftp
+service description;
+see
+.Xr services 5 .
+The server is normally started by
+.Xr inetd 8 .
+.Pp
+The use of
+.Xr tftp 1
+does not require an account or password on the remote system.
+Due to the lack of authentication information,
+.Nm
+will allow only publicly readable files to be
+accessed.
+Files containing the string
+.Dq Li "/../"
+or starting with
+.Dq Li "../"
+are not allowed.
+Files may be written only if they already exist (unless the
+.Fl w
+option is used) and are publicly writable (unless chrooted and the
+.Fl S
+option is used).
+Note that this extends the concept of
+.Dq public
+to include
+all users on all hosts that can be reached through the network;
+this may not be appropriate on all systems, and its implications
+should be considered before enabling tftp service.
+The server should have the user ID with the lowest possible privilege.
+.Pp
+Access to files may be restricted by invoking
+.Nm
+with a list of directories by including up to 20 pathnames
+as server program arguments in
+.Xr inetd.conf 5 .
+In this case access is restricted to files whose
+names are prefixed by the one of the given directories.
+The given directories are also treated as a search path for
+relative filename requests.
+.Pp
+The
+.Fl s
+option provides additional security by changing
+the root directory of
+.Nm ,
+thereby prohibiting accesses to outside of the specified
+.Ar directory .
+Because
+.Xr chroot 2
+requires super-user privileges,
+.Nm
+must be run as
+.Li root .
+However, after performing the
+.Xr chroot 2
+call,
+.Nm
+will set its user ID to that of the specified
+.Ar user ,
+or
+.Dq Li nobody
+if no
+.Fl u
+option is specified.
+.Pp
+The options are:
+.Bl -tag -width Ds
+.It Fl b
+By default,
+.Nm
+expects an initial message to be available on its input socket.
+If no data is available, the server exits immediately.
+If
+.Fl b
+is specified,
+.Nm
+will block waiting for the initial message.
+.It Fl c
+Changes the default root directory of a connecting host via
+.Xr chroot 2
+based on the connecting IP address.
+This prevents multiple clients from writing to the same file at the same time.
+If the directory does not exist, the client connection is refused.
+The
+.Fl s
+option is required for
+.Fl c
+and the specified
+.Ar directory
+is used as a base.
+.It Fl C
+Operates the same as
+.Fl c
+except it falls back to
+.Ar directory
+specified via
+.Fl s
+if a directory does not exist for the client's IP.
+.It Fl F
+Use this
+.Xr strftime 3
+compatible format string for the creation of the suffix if
+.Fl W
+is specified.
+By default the string "%Y%m%d" is used.
+.It Fl d, d Ar [value]
+Enables debug output.
+If
+.Ar value
+is not specified, then the debug level is increased by one
+for each instance of
+.Fl d
+which is specified.
+.Pp
+If
+.Ar value
+is specified, then the debug level is set to
+.Ar value .
+The debug level is a bitmask implemented in
+.Pa src/libexec/tftpd/tftp-utils.h .
+Valid values are 0 (DEBUG_NONE), 1 (DEBUG_PACKETS), 2, (DEBUG_SIMPLE),
+4 (DEBUG_OPTIONS), and 8 (DEBUG_ACCESS). Multiple debug values can be combined
+in the bitmask by logically OR'ing the values. For example, specifying
+.Fl d
+.Ar 15
+will enable all the debug values.
+.It Fl l
+Log all requests using
+.Xr syslog 3
+with the facility of
+.Dv LOG_FTP .
+.Sy Note :
+Logging of
+.Dv LOG_FTP
+messages
+must also be enabled in the syslog configuration file,
+.Xr syslog.conf 5 .
+.It Fl n
+Suppress negative acknowledgement of requests for nonexistent
+relative filenames.
+.It Fl o
+Disable support for RFC2347 style TFTP Options.
+.It Fl s Ar directory
+Cause
+.Nm
+to change its root directory to
+.Ar directory .
+After doing that but before accepting commands,
+.Nm
+will switch credentials to an unprivileged user.
+.It Fl S
+If
+.Nm
+runs chrooted, the option allows write requests according to generic
+file permissions, skipping requirement for files to be publicly writable.
+The option is ignored for non-chrooted run.
+.It Fl u Ar user
+Switch credentials to
+.Ar user
+(default
+.Dq Li nobody )
+when the
+.Fl s
+option is used.
+The user must be specified by name, not a numeric UID.
+.It Fl U Ar umask
+Set the
+.Ar umask
+for newly created files.
+The default is 022
+.Pq Dv S_IWGRP | S_IWOTH .
+.It Fl w
+Allow write requests to create new files.
+By default
+.Nm
+requires that the file specified in a write request exist.
+Note that this only works in directories writable by the user
+specified with
+.Fl u
+option
+.It Fl W
+As
+.Fl w
+but append a YYYYMMDD.nn sequence number to the end of the filename.
+Note that the string YYYYMMDD can be changed with the
+.Fl F
+option.
+.El
+.Sh SEE ALSO
+.Xr tftp 1 ,
+.Xr chroot 2 ,
+.Xr syslog 3 ,
+.Xr inetd.conf 5 ,
+.Xr services 5 ,
+.Xr syslog.conf 5 ,
+.Xr inetd 8
+.Pp
+The following RFC's are supported:
+.Rs
+.%T RFC 1350: The TFTP Protocol (Revision 2)
+.Re
+.Rs
+.%T RFC 2347: TFTP Option Extension
+.Re
+.Rs
+.%T RFC 2348: TFTP Blocksize Option
+.Re
+.Rs
+.%T RFC 2349: TFTP Timeout Interval and Transfer Size Options
+.Re
+.Rs
+.%T RFC 7440: TFTP Windowsize Option
+.Re
+.Pp
+The non-standard
+.Cm rollover
+and
+.Cm blksize2
+TFTP options are mentioned here:
+.Rs
+.%T Extending TFTP
+.%U http://www.compuphase.com/tftp.htm
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 ;
+the
+.Fl s
+option was introduced in
+.Fx 2.2 ,
+the
+.Fl u
+option was introduced in
+.Fx 4.2 ,
+the
+.Fl c
+option was introduced in
+.Fx 4.3 ,
+the
+.Fl F
+and
+.Fl W
+options were introduced in
+.Fx 7.4 ,
+and the
+.Fl S
+option was introduced in
+.Fx 13.3 .
+.Pp
+Support for Timeout Interval and Transfer Size Options (RFC2349)
+was introduced in
+.Fx 5.0 ,
+support for the TFTP Blocksize Option (RFC2348) and the blksize2 option
+was introduced in
+.Fx 7.4 .
+.Pp
+Edwin Groothuis <edwin@FreeBSD.org> performed a major rewrite of the
+.Nm
+and
+.Xr tftp 1
+code to support RFC2348.
+.Pp
+Support for the windowsize option (RFC7440) was introduced in
+.Fx 13.0 .
+.Sh NOTES
+Files larger than 33,553,919 octets (65535 blocks, last one <512
+octets) cannot be correctly transferred without client and server
+supporting blocksize negotiation (RFCs 2347 and 2348),
+or the non-standard TFTP rollover option.
+As a kludge,
+.Nm
+accepts a sequence of block number which wrap to zero after 65535,
+even if the rollover option is not specified.
+.Pp
+Many tftp clients will not transfer files over 16,776,703 octets
+(32767 blocks), as they incorrectly count the block number using
+a signed rather than unsigned 16-bit integer.
diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c
new file mode 100644
index 000000000000..a3faee86e7d0
--- /dev/null
+++ b/libexec/tftpd/tftpd.c
@@ -0,0 +1,849 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Trivial file transfer protocol server.
+ *
+ * This version includes many modifications by Jim Guyton
+ * <guyton@rand-unix>.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "tftp-file.h"
+#include "tftp-io.h"
+#include "tftp-utils.h"
+#include "tftp-transfer.h"
+#include "tftp-options.h"
+
+#ifdef LIBWRAP
+#include <tcpd.h>
+#endif
+
+static void tftp_wrq(int peer, char *, size_t);
+static void tftp_rrq(int peer, char *, size_t);
+
+/*
+ * Null-terminated directory prefix list for absolute pathname requests and
+ * search list for relative pathname requests.
+ *
+ * MAXDIRS should be at least as large as the number of arguments that
+ * inetd allows (currently 20).
+ */
+#define MAXDIRS 20
+static struct dirlist {
+ const char *name;
+ size_t len;
+} dirs[MAXDIRS+1];
+static int suppress_naks;
+static int logging;
+static int ipchroot;
+static int check_woth = 1;
+static int create_new = 0;
+static const char *newfile_format = "%Y%m%d";
+static int increase_name = 0;
+static mode_t mask = S_IWGRP | S_IWOTH;
+
+struct formats;
+static void tftp_recvfile(int peer, const char *mode);
+static void tftp_xmitfile(int peer, const char *mode);
+static int validate_access(int peer, char **, int);
+static char peername[NI_MAXHOST];
+
+static FILE *file;
+
+static struct formats {
+ const char *f_mode;
+ int f_convert;
+} formats[] = {
+ { "netascii", 1 },
+ { "octet", 0 },
+ { NULL, 0 }
+};
+
+int
+main(int argc, char *argv[])
+{
+ struct tftphdr *tp;
+ int peer;
+ socklen_t peerlen, len;
+ ssize_t n;
+ int ch;
+ char *chroot_dir = NULL;
+ struct passwd *nobody;
+ const char *chuser = "nobody";
+ char recvbuffer[MAXPKTSIZE];
+ int allow_ro = 1, allow_wo = 1, block = 0, on = 1;
+ pid_t pid;
+
+ tzset(); /* syslog in localtime */
+ acting_as_client = 0;
+
+ tftp_openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
+ while ((ch = getopt(argc, argv, "bcCd::F:lnoOp:s:Su:U:wW")) != -1) {
+ switch (ch) {
+ case 'b':
+ block = 1;
+ break;
+ case 'c':
+ ipchroot = 1;
+ break;
+ case 'C':
+ ipchroot = 2;
+ break;
+ case 'd':
+ if (optarg == NULL)
+ debug++;
+ else if (atoi(optarg) != 0)
+ debug += atoi(optarg);
+ else
+ debug |= debug_finds(optarg);
+ break;
+ case 'F':
+ newfile_format = optarg;
+ break;
+ case 'l':
+ logging = 1;
+ break;
+ case 'n':
+ suppress_naks = 1;
+ break;
+ case 'o':
+ options_rfc_enabled = 0;
+ break;
+ case 'O':
+ options_extra_enabled = 0;
+ break;
+ case 'p':
+ packetdroppercentage = (unsigned int)atoi(optarg);
+ tftp_log(LOG_INFO,
+ "Randomly dropping %d out of 100 packets",
+ packetdroppercentage);
+ break;
+ case 's':
+ chroot_dir = optarg;
+ break;
+ case 'S':
+ check_woth = -1;
+ break;
+ case 'u':
+ chuser = optarg;
+ break;
+ case 'U':
+ mask = strtol(optarg, NULL, 0);
+ break;
+ case 'w':
+ create_new = 1;
+ break;
+ case 'W':
+ create_new = 1;
+ increase_name = 1;
+ break;
+ default:
+ tftp_log(LOG_WARNING,
+ "ignoring unknown option -%c", ch);
+ }
+ }
+ if (optind < argc) {
+ struct dirlist *dirp;
+
+ /* Get list of directory prefixes. Skip relative pathnames. */
+ for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
+ optind++) {
+ if (argv[optind][0] == '/') {
+ dirp->name = argv[optind];
+ dirp->len = strlen(dirp->name);
+ dirp++;
+ }
+ }
+ }
+ else if (chroot_dir) {
+ dirs->name = "/";
+ dirs->len = 1;
+ }
+ if (ipchroot > 0 && chroot_dir == NULL) {
+ tftp_log(LOG_ERR, "-c requires -s");
+ exit(1);
+ }
+
+ umask(mask);
+
+ /* Find out who we are talking to and what we are going to do */
+ peerlen = sizeof(peer_sock);
+ n = recvfrom(0, recvbuffer, MAXPKTSIZE, block ? 0 : MSG_DONTWAIT,
+ (struct sockaddr *)&peer_sock, &peerlen);
+ if (n < 0) {
+ tftp_log(LOG_ERR, "recvfrom: %s", strerror(errno));
+ exit(1);
+ }
+ getnameinfo((struct sockaddr *)&peer_sock, peer_sock.ss_len,
+ peername, sizeof(peername), NULL, 0, NI_NUMERICHOST);
+ if ((size_t)n < 4 /* tftphdr */) {
+ tftp_log(LOG_ERR, "Rejecting %zd-byte request from %s",
+ n, peername);
+ exit(1);
+ }
+
+ if (ioctl(0, FIONBIO, &on) < 0) {
+ tftp_log(LOG_ERR, "ioctl(FIONBIO): %s", strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Now that we have read the message out of the UDP
+ * socket, we fork and exit. Thus, inetd will go back
+ * to listening to the tftp port, and the next request
+ * to come in will start up a new instance of tftpd.
+ *
+ * We do this so that inetd can run tftpd in "wait" mode.
+ * The problem with tftpd running in "nowait" mode is that
+ * inetd may get one or more successful "selects" on the
+ * tftp port before we do our receive, so more than one
+ * instance of tftpd may be started up. Worse, if tftpd
+ * break before doing the above "recvfrom", inetd would
+ * spawn endless instances, clogging the system.
+ */
+ pid = fork();
+ if (pid < 0) {
+ tftp_log(LOG_ERR, "fork: %s", strerror(errno));
+ exit(1);
+ } else if (pid != 0) {
+ exit(0);
+ }
+ /* child */
+
+#ifdef LIBWRAP
+ /*
+ * See if the client is allowed to talk to me.
+ * (This needs to be done before the chroot())
+ */
+ {
+ struct request_info req;
+
+ request_init(&req, RQ_CLIENT_ADDR, peername, 0);
+ request_set(&req, RQ_DAEMON, "tftpd", 0);
+
+ if (hosts_access(&req) == 0) {
+ if (debug & DEBUG_ACCESS)
+ tftp_log(LOG_WARNING,
+ "Access denied by 'tftpd' entry "
+ "in /etc/hosts.allow");
+
+ /*
+ * Full access might be disabled, but maybe the
+ * client is allowed to do read-only access.
+ */
+ request_set(&req, RQ_DAEMON, "tftpd-ro", 0);
+ allow_ro = hosts_access(&req);
+
+ request_set(&req, RQ_DAEMON, "tftpd-wo", 0);
+ allow_wo = hosts_access(&req);
+
+ if (allow_ro == 0 && allow_wo == 0) {
+ tftp_log(LOG_WARNING,
+ "Unauthorized access from %s", peername);
+ exit(1);
+ }
+
+ if (debug & DEBUG_ACCESS) {
+ if (allow_ro)
+ tftp_log(LOG_WARNING,
+ "But allowed readonly access "
+ "via 'tftpd-ro' entry");
+ if (allow_wo)
+ tftp_log(LOG_WARNING,
+ "But allowed writeonly access "
+ "via 'tftpd-wo' entry");
+ }
+ } else
+ if (debug & DEBUG_ACCESS)
+ tftp_log(LOG_WARNING,
+ "Full access allowed"
+ "in /etc/hosts.allow");
+ }
+#endif
+
+ /*
+ * Since we exit here, we should do that only after the above
+ * recvfrom to keep inetd from constantly forking should there
+ * be a problem. See the above comment about system clogging.
+ */
+ if (chroot_dir) {
+ if (ipchroot > 0) {
+ char *tempchroot;
+ struct stat sb;
+ int statret;
+ struct sockaddr_storage ss;
+ char hbuf[NI_MAXHOST];
+
+ statret = -1;
+ memcpy(&ss, &peer_sock, peer_sock.ss_len);
+ unmappedaddr((struct sockaddr_in6 *)&ss);
+ getnameinfo((struct sockaddr *)&ss, ss.ss_len,
+ hbuf, sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST);
+ asprintf(&tempchroot, "%s/%s", chroot_dir, hbuf);
+ if (ipchroot == 2)
+ statret = stat(tempchroot, &sb);
+ if (ipchroot == 1 ||
+ (statret == 0 && (sb.st_mode & S_IFDIR)))
+ chroot_dir = tempchroot;
+ }
+ /* Must get this before chroot because /etc might go away */
+ if ((nobody = getpwnam(chuser)) == NULL) {
+ tftp_log(LOG_ERR, "%s: no such user", chuser);
+ exit(1);
+ }
+ if (chroot(chroot_dir)) {
+ tftp_log(LOG_ERR, "chroot: %s: %s",
+ chroot_dir, strerror(errno));
+ exit(1);
+ }
+ if (chdir("/") != 0) {
+ tftp_log(LOG_ERR, "chdir: %s", strerror(errno));
+ exit(1);
+ }
+ if (setgroups(0, NULL) != 0) {
+ tftp_log(LOG_ERR, "setgroups failed");
+ exit(1);
+ }
+ if (setgid(nobody->pw_gid) != 0) {
+ tftp_log(LOG_ERR, "setgid failed");
+ exit(1);
+ }
+ if (setuid(nobody->pw_uid) != 0) {
+ tftp_log(LOG_ERR, "setuid failed");
+ exit(1);
+ }
+ if (check_woth == -1)
+ check_woth = 0;
+ }
+ if (check_woth == -1)
+ check_woth = 1;
+
+ len = sizeof(me_sock);
+ if (getsockname(0, (struct sockaddr *)&me_sock, &len) == 0) {
+ switch (me_sock.ss_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)&me_sock)->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)&me_sock)->sin6_port = 0;
+ break;
+ default:
+ /* unsupported */
+ break;
+ }
+ } else {
+ memset(&me_sock, 0, sizeof(me_sock));
+ me_sock.ss_family = peer_sock.ss_family;
+ me_sock.ss_len = peer_sock.ss_len;
+ }
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ peer = socket(peer_sock.ss_family, SOCK_DGRAM, 0);
+ if (peer < 0) {
+ tftp_log(LOG_ERR, "socket: %s", strerror(errno));
+ exit(1);
+ }
+ if (bind(peer, (struct sockaddr *)&me_sock, me_sock.ss_len) < 0) {
+ tftp_log(LOG_ERR, "bind: %s", strerror(errno));
+ exit(1);
+ }
+
+ tp = (struct tftphdr *)recvbuffer;
+ tp->th_opcode = ntohs(tp->th_opcode);
+ if (tp->th_opcode == RRQ) {
+ if (allow_ro)
+ tftp_rrq(peer, tp->th_stuff, (size_t)n - 1);
+ else {
+ tftp_log(LOG_WARNING,
+ "%s read access denied", peername);
+ exit(1);
+ }
+ } else if (tp->th_opcode == WRQ) {
+ if (allow_wo)
+ tftp_wrq(peer, tp->th_stuff, (size_t)n - 1);
+ else {
+ tftp_log(LOG_WARNING,
+ "%s write access denied", peername);
+ exit(1);
+ }
+ } else
+ send_error(peer, EBADOP);
+ exit(1);
+}
+
+static void
+reduce_path(char *fn)
+{
+ char *slash, *ptr;
+
+ /* Reduce all "/+./" to "/" (just in case we've got "/./../" later */
+ while ((slash = strstr(fn, "/./")) != NULL) {
+ for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
+ ;
+ slash += 2;
+ while (*slash)
+ *++ptr = *++slash;
+ }
+
+ /* Now reduce all "/something/+../" to "/" */
+ while ((slash = strstr(fn, "/../")) != NULL) {
+ if (slash == fn)
+ break;
+ for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
+ ;
+ for (ptr--; ptr >= fn; ptr--)
+ if (*ptr == '/')
+ break;
+ if (ptr < fn)
+ break;
+ slash += 3;
+ while (*slash)
+ *++ptr = *++slash;
+ }
+}
+
+static char *
+parse_header(int peer, char *recvbuffer, size_t size,
+ char **filename, char **mode)
+{
+ struct formats *pf;
+ char *cp;
+ size_t i;
+
+ *mode = NULL;
+ cp = recvbuffer;
+
+ i = get_field(peer, recvbuffer, size);
+ if (i >= PATH_MAX) {
+ tftp_log(LOG_ERR, "Bad option - filename too long");
+ send_error(peer, EBADOP);
+ exit(1);
+ }
+ *filename = recvbuffer;
+ tftp_log(LOG_INFO, "Filename: '%s'", *filename);
+ cp += i;
+
+ i = get_field(peer, cp, size);
+ *mode = cp;
+
+ /* Find the file transfer mode */
+ for (; *cp; cp++)
+ if (isupper((unsigned char)*cp))
+ *cp = tolower((unsigned char)*cp);
+ for (pf = formats; pf->f_mode; pf++)
+ if (strcmp(pf->f_mode, *mode) == 0)
+ break;
+ if (pf->f_mode == NULL) {
+ tftp_log(LOG_ERR,
+ "Bad option - Unknown transfer mode (%s)", *mode);
+ send_error(peer, EBADOP);
+ exit(1);
+ }
+ tftp_log(LOG_INFO, "Mode: '%s'", *mode);
+
+ return (cp + 1);
+}
+
+/*
+ * WRQ - receive a file from the client
+ */
+void
+tftp_wrq(int peer, char *recvbuffer, size_t size)
+{
+ char *cp;
+ int has_options = 0, ecode;
+ char *filename, *mode;
+ char fnbuf[PATH_MAX];
+
+ cp = parse_header(peer, recvbuffer, size, &filename, &mode);
+ size -= (cp - recvbuffer) + 1;
+
+ strlcpy(fnbuf, filename, sizeof(fnbuf));
+ reduce_path(fnbuf);
+ filename = fnbuf;
+
+ if (size > 0) {
+ if (options_rfc_enabled)
+ has_options = !parse_options(peer, cp, size);
+ else
+ tftp_log(LOG_INFO, "Options found but not enabled");
+ }
+
+ ecode = validate_access(peer, &filename, WRQ);
+ if (ecode == 0) {
+ if (has_options)
+ send_oack(peer);
+ else
+ send_ack(peer, 0);
+ }
+ if (logging) {
+ tftp_log(LOG_INFO, "%s: write request for %s: %s", peername,
+ filename, errtomsg(ecode));
+ }
+
+ if (ecode) {
+ send_error(peer, ecode);
+ exit(1);
+ }
+ tftp_recvfile(peer, mode);
+ exit(0);
+}
+
+/*
+ * RRQ - send a file to the client
+ */
+void
+tftp_rrq(int peer, char *recvbuffer, size_t size)
+{
+ char *cp;
+ int has_options = 0, ecode;
+ char *filename, *mode;
+ char fnbuf[PATH_MAX];
+
+ cp = parse_header(peer, recvbuffer, size, &filename, &mode);
+ size -= (cp - recvbuffer) + 1;
+
+ strlcpy(fnbuf, filename, sizeof(fnbuf));
+ reduce_path(fnbuf);
+ filename = fnbuf;
+
+ if (size > 0) {
+ if (options_rfc_enabled)
+ has_options = !parse_options(peer, cp, size);
+ else
+ tftp_log(LOG_INFO, "Options found but not enabled");
+ }
+
+ ecode = validate_access(peer, &filename, RRQ);
+ if (ecode == 0) {
+ if (has_options) {
+ int n;
+ char lrecvbuffer[MAXPKTSIZE];
+ struct tftphdr *rp = (struct tftphdr *)lrecvbuffer;
+
+ send_oack(peer);
+ n = receive_packet(peer, lrecvbuffer, MAXPKTSIZE,
+ NULL, timeoutpacket);
+ if (n < 0) {
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Aborting: %s",
+ rp_strerror(n));
+ return;
+ }
+ if (rp->th_opcode != ACK) {
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "Expected ACK, got %s on OACK",
+ packettype(rp->th_opcode));
+ return;
+ }
+ }
+ }
+
+ if (logging)
+ tftp_log(LOG_INFO, "%s: read request for %s: %s", peername,
+ filename, errtomsg(ecode));
+
+ if (ecode) {
+ /*
+ * Avoid storms of naks to a RRQ broadcast for a relative
+ * bootfile pathname from a diskless Sun.
+ */
+ if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
+ exit(0);
+ send_error(peer, ecode);
+ exit(1);
+ }
+ tftp_xmitfile(peer, mode);
+}
+
+/*
+ * Find the next value for YYYYMMDD.nn when the file to be written should
+ * be unique. Due to the limitations of nn, we will fail if nn reaches 100.
+ * Besides, that is four updates per hour on a file, which is kind of
+ * execessive anyway.
+ */
+static int
+find_next_name(char *filename, int *fd)
+{
+ /*
+ * GCC "knows" that we might write all of yyyymmdd plus the static
+ * elemenents in the format into into newname and thus complains
+ * unless we reduce the size. This array is still too big, but since
+ * the format is user supplied, it's not clear what a better limit
+ * value would be and this is sufficent to silence the warnings.
+ */
+ static const int suffix_len = strlen("..00");
+ char yyyymmdd[MAXPATHLEN - suffix_len];
+ char newname[MAXPATHLEN];
+ int i, ret;
+ time_t tval;
+ size_t len, namelen;
+ struct tm lt;
+
+ /* Create the YYYYMMDD part of the filename */
+ time(&tval);
+ lt = *localtime(&tval);
+ len = strftime(yyyymmdd, sizeof(yyyymmdd), newfile_format, &lt);
+ if (len == 0) {
+ syslog(LOG_WARNING,
+ "Filename suffix too long (%zu characters maximum)",
+ sizeof(yyyymmdd) - 1);
+ return (EACCESS);
+ }
+
+ /* Make sure the new filename is not too long */
+ namelen = strlen(filename);
+ if (namelen >= sizeof(newname) - len - suffix_len) {
+ syslog(LOG_WARNING,
+ "Filename too long (%zu characters, %zu maximum)",
+ namelen,
+ sizeof(newname) - len - suffix_len - 1);
+ return (EACCESS);
+ }
+
+ /* Find the first file which doesn't exist */
+ for (i = 0; i < 100; i++) {
+ ret = snprintf(newname, sizeof(newname), "%s.%s.%02d",
+ filename, yyyymmdd, i);
+ /*
+ * Size checked above so this can't happen, we'd use a
+ * (void) cast, but gcc intentionally ignores that if
+ * snprintf has __attribute__((warn_unused_result)).
+ */
+ if (ret < 0 || (size_t)ret >= sizeof(newname))
+ __unreachable();
+ *fd = open(newname, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (*fd > 0)
+ return 0;
+ }
+
+ return (EEXIST);
+}
+
+/*
+ * Validate file access. Since we
+ * have no uid or gid, for now require
+ * file to exist and be publicly
+ * readable/writable.
+ * If we were invoked with arguments
+ * from inetd then the file must also be
+ * in one of the given directory prefixes.
+ * Note also, full path name must be
+ * given as we have no login directory.
+ */
+int
+validate_access(int peer, char **filep, int mode)
+{
+ static char pathname[MAXPATHLEN];
+ struct stat sb;
+ struct dirlist *dirp;
+ char *filename = *filep;
+ int err, fd;
+
+ /*
+ * Prevent tricksters from getting around the directory restrictions
+ */
+ if (strncmp(filename, "../", 3) == 0 ||
+ strstr(filename, "/../") != NULL)
+ return (EACCESS);
+
+ if (*filename == '/') {
+ /*
+ * Absolute file name: allow the request if it's in one of the
+ * approved locations.
+ */
+ for (dirp = dirs; dirp->name != NULL; dirp++) {
+ if (dirp->len == 1)
+ /* Only "/" can have len 1 */
+ break;
+ if (strncmp(filename, dirp->name, dirp->len) == 0 &&
+ filename[dirp->len] == '/')
+ break;
+ }
+ /* If directory list is empty, allow access to any file */
+ if (dirp->name == NULL && dirp != dirs)
+ return (EACCESS);
+ if (stat(filename, &sb) != 0)
+ return (errno == ENOENT ? ENOTFOUND : EACCESS);
+ if (!S_ISREG(sb.st_mode))
+ return (ENOTFOUND);
+ if (mode == RRQ) {
+ if ((sb.st_mode & S_IROTH) == 0)
+ return (EACCESS);
+ } else {
+ if (check_woth && (sb.st_mode & S_IWOTH) == 0)
+ return (EACCESS);
+ }
+ } else {
+ /*
+ * Relative file name: search the approved locations for it.
+ * If the file exists in one of the directories and isn't
+ * readable, continue looking. However, change the error code
+ * to give an indication that the file exists.
+ */
+ err = ENOTFOUND;
+ for (dirp = dirs; dirp->name != NULL; dirp++) {
+ snprintf(pathname, sizeof(pathname), "%s/%s",
+ dirp->name, filename);
+ if (stat(pathname, &sb) != 0)
+ continue;
+ if (!S_ISREG(sb.st_mode))
+ continue;
+ err = EACCESS;
+ if (mode == RRQ) {
+ if ((sb.st_mode & S_IROTH) == 0)
+ continue;
+ } else {
+ if (check_woth && (sb.st_mode & S_IWOTH) == 0)
+ continue;
+ }
+ break;
+ }
+ if (dirp->name != NULL)
+ *filep = filename = pathname;
+ else if (mode == RRQ)
+ return (err);
+ else if (err != ENOTFOUND || !create_new)
+ return (err);
+ }
+
+ /*
+ * This option is handled here because it (might) require(s) the
+ * size of the file.
+ */
+ option_tsize(peer, NULL, mode, &sb);
+
+ if (mode == RRQ) {
+ fd = open(filename, O_RDONLY);
+ } else if (create_new) {
+ if (increase_name) {
+ err = find_next_name(filename, &fd);
+ if (err > 0)
+ return (err + 100);
+ } else {
+ fd = open(filename,
+ O_WRONLY | O_TRUNC | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IWGRP | S_IROTH | S_IWOTH );
+ }
+ } else {
+ fd = open(filename, O_WRONLY | O_TRUNC);
+ }
+ if (fd < 0)
+ return (errno + 100);
+ file = fdopen(fd, mode == RRQ ? "r" : "w");
+ if (file == NULL) {
+ close(fd);
+ return (errno + 100);
+ }
+ return (0);
+}
+
+static void
+tftp_xmitfile(int peer, const char *mode)
+{
+ uint16_t block;
+ time_t now;
+ struct tftp_stats ts;
+
+ memset(&ts, 0, sizeof(ts));
+ now = time(NULL);
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Transmitting file");
+
+ read_init(0, file, mode);
+ block = 1;
+ tftp_send(peer, &block, &ts);
+ read_close();
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_INFO, "Sent %jd bytes in %jd seconds",
+ (intmax_t)ts.amount, (intmax_t)time(NULL) - now);
+}
+
+static void
+tftp_recvfile(int peer, const char *mode)
+{
+ uint16_t block;
+ struct timeval now1, now2;
+ struct tftp_stats ts;
+
+ gettimeofday(&now1, NULL);
+ if (debug & DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Receiving file");
+
+ write_init(0, file, mode);
+
+ block = 0;
+ tftp_receive(peer, &block, &ts, NULL, 0);
+
+ gettimeofday(&now2, NULL);
+
+ if (debug & DEBUG_SIMPLE) {
+ double f;
+ if (now1.tv_usec > now2.tv_usec) {
+ now2.tv_usec += 1000000;
+ now2.tv_sec--;
+ }
+
+ f = now2.tv_sec - now1.tv_sec +
+ (now2.tv_usec - now1.tv_usec) / 100000.0;
+ tftp_log(LOG_INFO,
+ "Download of %jd bytes in %d blocks completed after %0.1f seconds\n",
+ (intmax_t)ts.amount, block, f);
+ }
+
+ return;
+}
diff --git a/libexec/ulog-helper/Makefile b/libexec/ulog-helper/Makefile
new file mode 100644
index 000000000000..61a88fb62692
--- /dev/null
+++ b/libexec/ulog-helper/Makefile
@@ -0,0 +1,8 @@
+PROG= ulog-helper
+BINOWN= root
+BINMODE=4555
+MAN=
+
+LIBADD= ulog
+
+.include <bsd.prog.mk>
diff --git a/libexec/ulog-helper/Makefile.depend b/libexec/ulog-helper/Makefile.depend
new file mode 100644
index 000000000000..9bdc0c195f2b
--- /dev/null
+++ b/libexec/ulog-helper/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libulog \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/ulog-helper/ulog-helper.c b/libexec/ulog-helper/ulog-helper.c
new file mode 100644
index 000000000000..e92ea87cbd59
--- /dev/null
+++ b/libexec/ulog-helper/ulog-helper.c
@@ -0,0 +1,97 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2009 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <ulog.h>
+
+/*
+ * This setuid helper utility writes user login records to disk.
+ * Unprivileged processes are not capable of writing records to utmpx,
+ * but we do want to allow this for pseudo-terminals. Because a file
+ * descriptor to a pseudo-terminal master device can only be obtained by
+ * processes using the pseudo-terminal, we expect such a descriptor on
+ * stdin.
+ *
+ * It uses the real user ID of the calling process to determine the
+ * username. It does allow users to log arbitrary hostnames.
+ */
+
+static const char *
+get_username(void)
+{
+ const struct passwd *pw;
+ const char *login;
+ uid_t uid;
+
+ /*
+ * Attempt to determine the username corresponding to this login
+ * session. First, validate the results of getlogin() against
+ * the password database. If getlogin() returns invalid data,
+ * return an arbitrary username corresponding to this uid.
+ */
+ uid = getuid();
+ if ((login = getlogin()) != NULL && (pw = getpwnam(login)) != NULL &&
+ pw->pw_uid == uid)
+ return (login);
+ if ((pw = getpwuid(uid)) != NULL)
+ return (pw->pw_name);
+ return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *line, *user, *host;
+
+ /* Device line name. */
+ if ((line = ptsname(STDIN_FILENO)) == NULL)
+ return (EX_USAGE);
+
+ if ((argc == 2 || argc == 3) && strcmp(argv[1], "login") == 0) {
+ /* Username. */
+ user = get_username();
+ if (user == NULL)
+ return (EX_OSERR);
+
+ /* Hostname. */
+ host = argc == 3 ? argv[2] : NULL;
+
+ ulog_login(line, user, host);
+ return (EX_OK);
+ } else if (argc == 2 && strcmp(argv[1], "logout") == 0) {
+ ulog_logout(line);
+ return (EX_OK);
+ }
+
+ return (EX_USAGE);
+}
diff --git a/libexec/ypxfr/Makefile b/libexec/ypxfr/Makefile
new file mode 100644
index 000000000000..e93df9a8b5ec
--- /dev/null
+++ b/libexec/ypxfr/Makefile
@@ -0,0 +1,45 @@
+PACKAGE= yp
+PROG= ypxfr
+SRCS= yp_dblookup.c yp_dbwrite.c yp_error.c \
+ ypxfr_getmap.c ypxfr_main.c ypxfr_misc.c \
+ ypxfrd_getmap.c \
+ ${GENSRCS}
+GENSRCS=yp.h yp_clnt.c ypxfr_clnt.c
+
+.PATH: ${SRCTOP}/usr.sbin/ypserv
+
+MAN= ypxfr.8
+
+CFLAGS+= -I.
+
+WARNS?= 2
+WFORMAT=0
+
+LIBADD= rpcsvc
+
+CLEANFILES= ${GENSRCS}
+
+RPCDIR= ${SRCTOP}/include/rpcsvc
+RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -I -C
+
+ypxfr_clnt.c: ${RPCDIR}/yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -DYPPUSH_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp_clnt.c: ${RPCDIR}/yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -DYPSERV_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp.h: ${RPCDIR}/yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yp.x
+
+# ypxfrd_xdr.c: ${RPCDIR}/ypxfrd.x
+# rm -f ${.TARGET}
+# ${RPCGEN} -c -o ${.TARGET} ${RPCDIR}/ypxfrd.x
+
+ypxfrd.h: ${RPCDIR}/ypxfrd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/ypxfrd.x
+
+.include <bsd.prog.mk>
diff --git a/libexec/ypxfr/Makefile.depend b/libexec/ypxfr/Makefile.depend
new file mode 100644
index 000000000000..210baeaabfd6
--- /dev/null
+++ b/libexec/ypxfr/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/rpc \
+ include/rpcsvc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/librpcsvc \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/libexec/ypxfr/yp_dbwrite.c b/libexec/ypxfr/yp_dbwrite.c
new file mode 100644
index 000000000000..30c52c3cb88b
--- /dev/null
+++ b/libexec/ypxfr/yp_dbwrite.c
@@ -0,0 +1,110 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <db.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <rpcsvc/yp.h>
+#include "ypxfr_extern.h"
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+
+/*
+ * Open a DB database read/write
+ */
+DB *
+yp_open_db_rw(const char *domain, const char *map, const int flags)
+{
+ DB *dbp;
+ char buf[1025];
+
+
+ yp_errno = YP_TRUE;
+
+ if (map[0] == '.' || strchr(map, '/')) {
+ yp_errno = YP_BADARGS;
+ return (NULL);
+ }
+
+#define FLAGS O_RDWR|O_EXLOCK|O_EXCL|O_CREAT
+
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
+ dbp = dbopen(buf,flags ? flags : FLAGS,PERM_SECURE,DB_HASH,&openinfo);
+
+ if (dbp == NULL) {
+ switch (errno) {
+ case ENOENT:
+ yp_errno = YP_NOMAP;
+ break;
+ case EFTYPE:
+ yp_errno = YP_BADDB;
+ break;
+ default:
+ yp_errno = YP_YPERR;
+ break;
+ }
+ }
+
+ return (dbp);
+}
+
+int
+yp_put_record(DB *dbp, DBT *key, DBT *data, int allow_overwrite)
+{
+ int rval;
+
+ if ((rval = (dbp->put)(dbp,key,data, allow_overwrite ? 0 :
+ R_NOOVERWRITE))) {
+ switch (rval) {
+ case 1:
+ return(YP_FALSE);
+ break;
+ case -1:
+ default:
+ (void)(dbp->close)(dbp);
+ return(YP_BADDB);
+ break;
+ }
+ }
+
+ return(YP_TRUE);
+}
diff --git a/libexec/ypxfr/ypxfr.8 b/libexec/ypxfr/ypxfr.8
new file mode 100644
index 000000000000..bb07da5978ed
--- /dev/null
+++ b/libexec/ypxfr/ypxfr.8
@@ -0,0 +1,306 @@
+.\" Copyright (c) 1995
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd February 5, 1995
+.Dt YPXFR 8
+.Os
+.Sh NAME
+.Nm ypxfr
+.Nd "transfer NIS database from remote server to local host"
+.Sh SYNOPSIS
+.Nm /usr/libexec/ypxfr
+.Op Fl f
+.Op Fl c
+.Op Fl d Ar target domain
+.Op Fl h Ar source host
+.Op Fl s Ar source domain
+.Op Fl p Ar path
+.Op Fl C Ar taskid program-number ipaddr port
+.Ar mapname
+.Sh DESCRIPTION
+The
+.Nm
+utility copies an
+.Tn NIS
+database (or
+.Pa map )
+from one
+.Tn NIS
+server to another using
+.Tn NIS
+services.
+In
+.Fx ,
+.Nm
+is generally invoked by
+.Xr ypserv 8
+when it receives a map transfer request from
+.Xr yppush 8 .
+The
+.Nm
+utility is used primarily in environments where several
+.Tn NIS
+servers are in use in a single domain.
+One server, the
+.Tn NIS
+master, maintains
+the canonical copies of all
+.Tn NIS
+maps, and all the other servers,
+the
+.Tn NIS
+slaves, copy new versions of the maps from the master whenever
+any updates are made (i.e., when a user updates their password via
+.Xr yppasswd 1 ) .
+.Pp
+When run,
+.Nm
+creates a temporary database file in
+.Pa /var/yp/[domainname] ,
+and fills it with the contents of
+.Ar mapname
+as supplied by the specified
+.Ar source host .
+When the entire map has been transferred,
+.Nm
+deletes the original copy of
+.Ar mapname
+and moves the temporary copy into its place.
+When the transfer is
+complete,
+.Nm
+will attempt to send a 'clear current map' request to the local
+.Xr ypserv 8
+process to clear any possible references it may still have to the
+stale map.
+.Pp
+Note that all files created by
+.Nm
+are owner readable and writable only for security reasons.
+Since the
+.Tn NIS
+maps and the directory in which they reside are normally owned by
+root, this prevents non-privileged users from making unauthorized
+modifications.
+.Pp
+In order to maintain consistency across all
+.Tn NIS
+servers,
+.Nm
+can be run periodically in a
+.Xr cron 8
+job.
+Maps which change infrequently
+need only be updated once a day (preferably late at night when system
+usage is lowest), whereas those that are subject to frequent changes
+(such a
+.Pa passwd.byname
+and
+.Pa passwd.byuid )
+should be updated perhaps once every hour.
+Using
+.Xr cron 8
+to automatically
+update the
+.Tn NIS
+maps is not strictly mandatory since all updates should
+be propagated by
+.Xr yppush 8
+when
+.Pa /var/yp/Makefile
+is run on the
+.Tn NIS
+master server, however it is good practice
+on large networks where possible outages could cause
+.Tn NIS
+servers to fall out of sync with each other.
+.Pp
+When
+.Nm
+is invoked without a controlling terminal, e.g.\& from inside
+.Xr ypserv 8 ,
+it logs all its output using the
+.Xr syslog 3
+facility.
+.Sh NOTES
+The
+.Fx
+version of
+.Nm
+has support for a special map transfer protocol which works in
+conjunction with the
+.Fx
+.Xr rpc.ypxfrd 8
+server.
+This protocol allows it to transfer raw map database files from
+the
+.Tn NIS
+master server and can be many times faster than the standard
+transfer method, particularly for very large
+.Tn NIS
+maps.
+The
+.Nm
+utility will check to see if the
+.Xr rpc.ypxfrd 8
+server is registered on the
+.Tn NIS
+master server and attempt to use
+it if it is present.
+If it is not it will fall back to the standard
+transfer method, copying the map contents from
+.Xr ypserv 8
+and creating new maps instead.
+.Pp
+Note that while the
+.Fx
+ypxfrd protocol is conceptually similar
+to the SunOS ypxfrd protocol,
+the
+.Fx
+protocol is not compatible with
+Sun's, therefore it will not work with Sun's ypxfrd server.
+.Fx
+slave systems can still transfer maps from any
+.No non- Ns Fx
+.Tn NIS
+server,
+however they will only be able to take advantage of the faster protocol
+if the master server is also running
+.Fx .
+.Sh OPTIONS
+The following options and flags are supported by
+.Nm :
+.Bl -tag -width indent
+.It Fl f
+Force a map transfer.
+Normally,
+.Nm
+will not transfer a map if it determines that the
+.Tn NIS
+master's copy
+is not newer than the existing copy already on the local host: the
+.Fl f
+flag forces a transfer regardless of which server's version is more recent.
+.It Fl c
+Do not send a 'clear current map' request to the
+.Xr ypserv 8
+process running on the local host.
+This flag is normally used when
+invoking
+.Nm
+manually on a machine that is not yet running
+.Xr ypserv 8 .
+Without this flag, failure to contact the local
+.Tn NIS
+server will cause
+.Nm
+to abort the transfer.
+.It Fl d Ar target domain
+Specify a target domain other than the current
+.Tn NIS
+domain.
+.It Fl h Ar source host
+Specify the name of the host from which to copy the
+.Tn NIS
+maps.
+This option
+is used to ensure that
+.Nm
+only copies maps from the
+.Tn NIS
+master server.
+.It Fl s Ar source domain
+Specify the domain from which to transfer a map, in the event that
+the transfer is being done across two different
+.Tn NIS
+domains.
+.It Fl p Ar path
+Specify the top level directory containing the
+.Tn NIS
+maps.
+By
+default, this path is
+.Pa /var/yp .
+The
+.Fl p
+flag allows you to specify an alternate path should you wish to
+store your
+.Tn NIS
+maps in a different part of the file system.
+The
+.Tn NIS
+server,
+.Xr ypserv 8 ,
+passes this flag to
+.Nm
+if it too has been told to use an alternate path.
+.It Fl C Ar taskid program-number ipaddr port
+These options are used only when
+.Nm
+is invoked by
+.Xr ypserv 8
+in response to a map transfer request initiated by
+.Xr yppush 8 .
+In this instance,
+.Nm
+needs to 'callback' to the
+.Xr yppush 8
+process and interact with it, so
+.Xr yppush 8
+passes to it an IP address
+.Ar ipaddr ,
+port number
+.Ar port ,
+registered program number
+.Ar program-number
+and a transaction ID
+.Ar taskid
+that it can use to contact the waiting
+.Xr yppush 8
+process on the master server.
+.It Ar mapname
+The name of the map to transfer.
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/[maps]
+The
+.Tn NIS
+maps for a particular
+.Tn NIS
+domain.
+.El
+.Sh SEE ALSO
+.Xr yp 8 ,
+.Xr yppush 8 ,
+.Xr ypserv 8
+.Sh AUTHORS
+.An Bill Paul Aq Mt wpaul@ctr.columbia.edu
diff --git a/libexec/ypxfr/ypxfr_extern.h b/libexec/ypxfr/ypxfr_extern.h
new file mode 100644
index 000000000000..af9134bb4339
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_extern.h
@@ -0,0 +1,62 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <limits.h>
+#include <paths.h>
+#include <db.h>
+#include <rpcsvc/yp.h>
+
+extern HASHINFO openinfo;
+extern BTREEINFO openinfo_b;
+
+#ifndef _PATH_YP
+#define _PATH_YP "/var/yp/"
+#endif
+
+extern char *yp_dir;
+extern int debug;
+extern enum ypstat yp_errno;
+extern void yp_error(const char *, ...);
+extern int _yp_check(char **);
+extern const char *ypxfrerr_string(ypxfrstat);
+extern DB *yp_open_db_rw(const char *, const char *, const int);
+extern void yp_init_dbs(void);
+extern int yp_put_record(DB *, DBT *, DBT *, int);
+extern int yp_get_record(const char *, const char *, const DBT *, DBT *, int);
+extern int ypxfr_get_map(char *, char *, char *, int (*)(int, char *, int, char *, int, char*));
+extern char *ypxfr_get_master(char *, char *, char *, const int);
+extern unsigned long ypxfr_get_order(char *, char *, char *, const int);
+extern int ypxfr_match(char *, char *, char *, char *, unsigned long);
+extern char *ypxfxerr_string(ypxfrstat);
+extern int ypxfrd_get_map(char *, char *, char *, char *);
diff --git a/libexec/ypxfr/ypxfr_getmap.c b/libexec/ypxfr/ypxfr_getmap.c
new file mode 100644
index 000000000000..83b860dd75b2
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_getmap.c
@@ -0,0 +1,99 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp.h>
+#include "ypxfr_extern.h"
+
+extern bool_t xdr_ypresp_all_seq(XDR *, unsigned long *);
+
+extern int (*ypresp_allfn)();
+extern void *ypresp_data;
+extern DB *specdbp;
+extern enum ypstat yp_errno;
+
+/*
+ * This is largely the same as yp_all() except we do the transfer
+ * from a specific server without the aid of ypbind(8). We need to
+ * be able to specify the source host explicitly since ypxfr may
+ * only transfer maps from the NIS master server for any given domain.
+ * However, if we use the libc version of yp_all(), we could end up
+ * talking to one of the slaves instead. We do need to dig into libc
+ * a little though, since it contains the magic XDR function we need.
+ */
+int
+ypxfr_get_map(char *map, char *domain, char *host,
+ int (*callback)(int, char *, int, char *, int, char*))
+{
+ CLIENT *clnt;
+ ypreq_nokey req;
+ unsigned long status;
+ struct timeval timeout;
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 10;
+
+ /* YPPROC_ALL is a TCP service */
+ if ((clnt = clnt_create(host, YPPROG, YPVERS, "tcp")) == NULL) {
+ yp_error("%s", clnt_spcreateerror("failed to \
+create tcp handle"));
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ return(1);
+ }
+
+ req.domain = domain;
+ req.map = map;
+ ypresp_allfn = callback;
+ ypresp_data = NULL;
+
+ (void)clnt_call(clnt, YPPROC_ALL, (xdrproc_t)xdr_ypreq_nokey, &req,
+ (xdrproc_t)xdr_ypresp_all_seq, &status, timeout);
+
+ clnt_destroy(clnt);
+
+ if (status == YP_NOMORE)
+ return(0);
+
+ if (status != YP_TRUE) {
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ return(1);
+ }
+
+ return(0);
+}
diff --git a/libexec/ypxfr/ypxfr_main.c b/libexec/ypxfr/ypxfr_main.c
new file mode 100644
index 000000000000..409d37fe15f4
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_main.c
@@ -0,0 +1,577 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/clnt.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/ypxfrd.h>
+#include "ypxfr_extern.h"
+
+int debug = 1;
+
+char *progname = "ypxfr";
+char *yp_dir = _PATH_YP;
+int _rpcpmstart = 0;
+static int ypxfr_use_yplib = 0; /* Assume the worst. */
+static int ypxfr_clear = 1;
+static int ypxfr_prognum = 0;
+static struct sockaddr_in ypxfr_callback_addr;
+static struct yppushresp_xfr ypxfr_resp;
+static DB *dbp;
+
+static void
+ypxfr_exit(ypxfrstat retval, char *temp)
+{
+ CLIENT *clnt;
+ int sock = RPC_ANYSOCK;
+ struct timeval timeout;
+
+ /* Clean up no matter what happened previously. */
+ if (temp != NULL) {
+ if (dbp != NULL)
+ (void)(dbp->close)(dbp);
+ if (unlink(temp) == -1) {
+ yp_error("failed to unlink %s",strerror(errno));
+ }
+ }
+
+ if (ypxfr_prognum) {
+ timeout.tv_sec = 20;
+ timeout.tv_usec = 0;
+
+ if ((clnt = clntudp_create(&ypxfr_callback_addr, ypxfr_prognum,
+ 1, timeout, &sock)) == NULL) {
+ yp_error("%s", clnt_spcreateerror("failed to "
+ "establish callback handle"));
+ exit(1);
+ }
+
+ ypxfr_resp.status = (yppush_status)retval;
+
+ if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) {
+ yp_error("%s", clnt_sperror(clnt, "callback failed"));
+ clnt_destroy(clnt);
+ exit(1);
+ }
+ clnt_destroy(clnt);
+ } else {
+ yp_error("Exiting: %s", ypxfrerr_string(retval));
+ }
+
+ exit(0);
+}
+
+static void
+usage(void)
+{
+ if (_rpcpmstart) {
+ ypxfr_exit(YPXFR_BADARGS,NULL);
+ } else {
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: ypxfr [-f] [-c] [-d target domain] [-h source host]",
+ " [-s source domain] [-p path]",
+ " [-C taskid program-number ipaddr port] mapname");
+ exit(1);
+ }
+}
+
+int
+ypxfr_foreach(int status, char *key, int keylen, char *val, int vallen,
+ char *data)
+{
+ DBT dbkey, dbval;
+
+ if (status != YP_TRUE)
+ return (status);
+
+ /*
+ * XXX Do not attempt to write zero-length keys or
+ * data into a Berkeley DB hash database. It causes a
+ * strange failure mode where sequential searches get
+ * caught in an infinite loop.
+ */
+ if (keylen) {
+ dbkey.data = key;
+ dbkey.size = keylen;
+ } else {
+ dbkey.data = "";
+ dbkey.size = 1;
+ }
+ if (vallen) {
+ dbval.data = val;
+ dbval.size = vallen;
+ } else {
+ dbval.data = "";
+ dbval.size = 1;
+ }
+
+ if (yp_put_record(dbp, &dbkey, &dbval, 0) != YP_TRUE)
+ return(yp_errno);
+
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ int ypxfr_force = 0;
+ char *ypxfr_dest_domain = NULL;
+ char *ypxfr_source_host = NULL;
+ char *ypxfr_source_domain = NULL;
+ char *ypxfr_local_domain = NULL;
+ char *ypxfr_master = NULL;
+ unsigned long ypxfr_order = -1, ypxfr_skew_check = -1;
+ char *ypxfr_mapname = NULL;
+ int ypxfr_args = 0;
+ char ypxfr_temp_map[MAXPATHLEN + 2];
+ char tempmap[MAXPATHLEN + 2];
+ char buf[MAXPATHLEN + 2];
+ DBT key, data;
+ int remoteport;
+ int interdom = 0;
+ int secure = 0;
+
+ if (!isatty(fileno(stderr))) {
+ openlog("ypxfr", LOG_PID, LOG_DAEMON);
+ _rpcpmstart = 1;
+ }
+
+ if (argc < 2)
+ usage();
+
+ while ((ch = getopt(argc, argv, "fcd:h:s:p:C:")) != -1) {
+ int my_optind;
+ switch (ch) {
+ case 'f':
+ ypxfr_force++;
+ ypxfr_args++;
+ break;
+ case 'c':
+ ypxfr_clear = 0;
+ ypxfr_args++;
+ break;
+ case 'd':
+ ypxfr_dest_domain = optarg;
+ ypxfr_args += 2;
+ break;
+ case 'h':
+ ypxfr_source_host = optarg;
+ ypxfr_args += 2;
+ break;
+ case 's':
+ ypxfr_source_domain = optarg;
+ ypxfr_args += 2;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ ypxfr_args += 2;
+ break;
+ case 'C':
+ /*
+ * Whoever decided that the -C flag should take
+ * four arguments is a twit.
+ */
+ my_optind = optind - 1;
+ if (argv[my_optind] == NULL || !strlen(argv[my_optind])) {
+ yp_error("transaction ID not specified");
+ usage();
+ }
+ ypxfr_resp.transid = atol(argv[my_optind]);
+ my_optind++;
+ if (argv[my_optind] == NULL || !strlen(argv[my_optind])) {
+ yp_error("RPC program number not specified");
+ usage();
+ }
+ ypxfr_prognum = atol(argv[my_optind]);
+ my_optind++;
+ if (argv[my_optind] == NULL || !strlen(argv[my_optind])) {
+ yp_error("address not specified");
+ usage();
+ }
+ if (!inet_aton(argv[my_optind], &ypxfr_callback_addr.sin_addr)) {
+ yp_error("failed to convert '%s' to IP addr",
+ argv[my_optind]);
+ exit(1);
+ }
+ my_optind++;
+ if (argv[my_optind] == NULL || !strlen(argv[my_optind])) {
+ yp_error("port not specified");
+ usage();
+ }
+ ypxfr_callback_addr.sin_port = htons((u_short)atoi(argv[my_optind]));
+ ypxfr_args += 5;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ ypxfr_mapname = argv[ypxfr_args + 1];
+
+ if (ypxfr_mapname == NULL) {
+ yp_error("no map name specified");
+ usage();
+ }
+
+ /* Always the case. */
+ ypxfr_callback_addr.sin_family = AF_INET;
+
+ /* Determine if local NIS client facilities are turned on. */
+ if (!yp_get_default_domain(&ypxfr_local_domain) &&
+ _yp_check(&ypxfr_local_domain))
+ ypxfr_use_yplib = 1;
+
+ /*
+ * If no destination domain is specified, assume that the
+ * local default domain is to be used and try to obtain it.
+ * Fails if NIS client facilities are turned off.
+ */
+ if (ypxfr_dest_domain == NULL) {
+ if (ypxfr_use_yplib) {
+ yp_get_default_domain(&ypxfr_dest_domain);
+ } else {
+ yp_error("no destination domain specified and \
+the local domain name isn't set");
+ ypxfr_exit(YPXFR_BADARGS,NULL);
+ }
+ }
+
+ /*
+ * If a source domain is not specified, assume it to
+ * be the same as the destination domain.
+ */
+ if (ypxfr_source_domain == NULL) {
+ ypxfr_source_domain = ypxfr_dest_domain;
+ }
+
+ /*
+ * If the source host is not specified, assume it to be the
+ * master for the specified map. If local NIS client facilities
+ * are turned on, we can figure this out using yp_master().
+ * If not, we have to see if a local copy of the map exists
+ * and extract its YP_MASTER_NAME record. If _that_ fails,
+ * we are stuck and must ask the user for more information.
+ */
+ if (ypxfr_source_host == NULL) {
+ if (!ypxfr_use_yplib) {
+ /*
+ * Double whammy: NIS isn't turned on and the user
+ * didn't specify a source host.
+ */
+ char *dptr;
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+
+ if (yp_get_record(ypxfr_dest_domain, ypxfr_mapname,
+ &key, &data, 1) != YP_TRUE) {
+ yp_error("no source host specified");
+ ypxfr_exit(YPXFR_BADARGS,NULL);
+ }
+ dptr = data.data;
+ dptr[data.size] = '\0';
+ ypxfr_master = ypxfr_source_host = strdup(dptr);
+ }
+ } else {
+ if (ypxfr_use_yplib)
+ ypxfr_use_yplib = 0;
+ }
+
+ if (ypxfr_master == NULL) {
+ if ((ypxfr_master = ypxfr_get_master(ypxfr_source_domain,
+ ypxfr_mapname,
+ ypxfr_source_host,
+ ypxfr_use_yplib)) == NULL) {
+ yp_error("failed to find master of %s in domain %s: %s",
+ ypxfr_mapname, ypxfr_source_domain,
+ ypxfrerr_string((ypxfrstat)yp_errno));
+ ypxfr_exit(YPXFR_MADDR,NULL);
+ }
+ }
+
+ /*
+ * If we got here and ypxfr_source_host is still undefined,
+ * it means we had to resort to using yp_master() to find the
+ * master server for the map. The source host and master should
+ * be identical.
+ */
+ if (ypxfr_source_host == NULL)
+ ypxfr_source_host = ypxfr_master;
+
+ /*
+ * Don't talk to ypservs on unprivileged ports.
+ */
+ remoteport = getrpcport(ypxfr_source_host, YPPROG, YPVERS, IPPROTO_UDP);
+ if (remoteport >= IPPORT_RESERVED) {
+ yp_error("ypserv on %s not running on reserved port",
+ ypxfr_source_host);
+ ypxfr_exit(YPXFR_REFUSED, NULL);
+ }
+
+ if ((ypxfr_order = ypxfr_get_order(ypxfr_source_domain,
+ ypxfr_mapname,
+ ypxfr_master, 0)) == 0) {
+ yp_error("failed to get order number of %s: %s",
+ ypxfr_mapname, yp_errno == YP_TRUE ?
+ "map has order 0" :
+ ypxfrerr_string((ypxfrstat)yp_errno));
+ ypxfr_exit(YPXFR_YPERR,NULL);
+ }
+
+ if (ypxfr_match(ypxfr_master, ypxfr_source_domain, ypxfr_mapname,
+ "YP_INTERDOMAIN", sizeof("YP_INTERDOMAIN") - 1))
+ interdom++;
+
+ if (ypxfr_match(ypxfr_master, ypxfr_source_domain, ypxfr_mapname,
+ "YP_SECURE", sizeof("YP_SECURE") - 1))
+ secure++;
+
+ key.data = "YP_LAST_MODIFIED";
+ key.size = sizeof("YP_LAST_MODIFIED") - 1;
+
+ /* The order number is immaterial when the 'force' flag is set. */
+
+ if (!ypxfr_force) {
+ int ignore = 0;
+ if (yp_get_record(ypxfr_dest_domain,ypxfr_mapname,&key,&data,1) != YP_TRUE) {
+ switch (yp_errno) {
+ case YP_NOKEY:
+ ypxfr_exit(YPXFR_FORCE,NULL);
+ break;
+ case YP_NOMAP:
+ /*
+ * If the map doesn't exist, we're
+ * creating it. Ignore the error.
+ */
+ ignore++;
+ break;
+ case YP_BADDB:
+ default:
+ ypxfr_exit(YPXFR_DBM,NULL);
+ break;
+ }
+ }
+ if (!ignore && ypxfr_order <= atoi(data.data))
+ ypxfr_exit(YPXFR_AGE, NULL);
+
+ }
+
+ /* Construct a temporary map file name */
+ snprintf(tempmap, sizeof(tempmap), "%s.%d",ypxfr_mapname, getpid());
+ snprintf(ypxfr_temp_map, sizeof(ypxfr_temp_map), "%s/%s/%s", yp_dir,
+ ypxfr_dest_domain, tempmap);
+
+ if ((remoteport = getrpcport(ypxfr_source_host, YPXFRD_FREEBSD_PROG,
+ YPXFRD_FREEBSD_VERS, IPPROTO_TCP))) {
+
+ /* Don't talk to rpc.ypxfrds on unprovileged ports. */
+ if (remoteport >= IPPORT_RESERVED) {
+ yp_error("rpc.ypxfrd on %s not using privileged port",
+ ypxfr_source_host);
+ ypxfr_exit(YPXFR_REFUSED, NULL);
+ }
+
+ /* Try to send using ypxfrd. If it fails, use old method. */
+ if (!ypxfrd_get_map(ypxfr_source_host, ypxfr_mapname,
+ ypxfr_source_domain, ypxfr_temp_map))
+ goto leave;
+ }
+
+ /* Open the temporary map read/write. */
+ if ((dbp = yp_open_db_rw(ypxfr_dest_domain, tempmap, 0)) == NULL) {
+ yp_error("failed to open temporary map file");
+ ypxfr_exit(YPXFR_DBM,NULL);
+ }
+
+ /*
+ * Fill in the keys we already know, such as the order number,
+ * master name, input file name (we actually make up a bogus
+ * name for that) and output file name.
+ */
+ snprintf(buf, sizeof(buf), "%lu", ypxfr_order);
+ data.data = buf;
+ data.size = strlen(buf);
+
+ if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) {
+ yp_error("failed to write order number to database");
+ ypxfr_exit(YPXFR_DBM,ypxfr_temp_map);
+ }
+
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+ data.data = ypxfr_master;
+ data.size = strlen(ypxfr_master);
+
+ if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) {
+ yp_error("failed to write master name to database");
+ ypxfr_exit(YPXFR_DBM,ypxfr_temp_map);
+ }
+
+ key.data = "YP_DOMAIN_NAME";
+ key.size = sizeof("YP_DOMAIN_NAME") - 1;
+ data.data = ypxfr_dest_domain;
+ data.size = strlen(ypxfr_dest_domain);
+
+ if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) {
+ yp_error("failed to write domain name to database");
+ ypxfr_exit(YPXFR_DBM,ypxfr_temp_map);
+ }
+
+ snprintf (buf, sizeof(buf), "%s:%s", ypxfr_source_host, ypxfr_mapname);
+
+ key.data = "YP_INPUT_NAME";
+ key.size = sizeof("YP_INPUT_NAME") - 1;
+ data.data = &buf;
+ data.size = strlen(buf);
+
+ if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) {
+ yp_error("failed to write input name to database");
+ ypxfr_exit(YPXFR_DBM,ypxfr_temp_map);
+
+ }
+
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, ypxfr_dest_domain,
+ ypxfr_mapname);
+
+ key.data = "YP_OUTPUT_NAME";
+ key.size = sizeof("YP_OUTPUT_NAME") - 1;
+ data.data = &buf;
+ data.size = strlen(buf);
+
+ if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) {
+ yp_error("failed to write output name to database");
+ ypxfr_exit(YPXFR_DBM,ypxfr_temp_map);
+ }
+
+ if (interdom) {
+ key.data = "YP_INTERDOMAIN";
+ key.size = sizeof("YP_INTERDOMAIN") - 1;
+ data.data = "";
+ data.size = 0;
+
+ if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) {
+ yp_error("failed to add interdomain flag to database");
+ ypxfr_exit(YPXFR_DBM,ypxfr_temp_map);
+ }
+ }
+
+ if (secure) {
+ key.data = "YP_SECURE";
+ key.size = sizeof("YP_SECURE") - 1;
+ data.data = "";
+ data.size = 0;
+
+ if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) {
+ yp_error("failed to add secure flag to database");
+ ypxfr_exit(YPXFR_DBM,ypxfr_temp_map);
+ }
+ }
+
+ /* Now suck over the contents of the map from the master. */
+
+ if (ypxfr_get_map(ypxfr_mapname,ypxfr_source_domain,
+ ypxfr_source_host, ypxfr_foreach)){
+ yp_error("failed to retrieve map from source host");
+ ypxfr_exit(YPXFR_YPERR,ypxfr_temp_map);
+ }
+
+ (void)(dbp->close)(dbp);
+ dbp = NULL; /* <- yes, it seems this is necessary. */
+
+leave:
+
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, ypxfr_dest_domain,
+ ypxfr_mapname);
+
+ /* Peek at the order number again and check for skew. */
+ if ((ypxfr_skew_check = ypxfr_get_order(ypxfr_source_domain,
+ ypxfr_mapname,
+ ypxfr_master, 0)) == 0) {
+ yp_error("failed to get order number of %s: %s",
+ ypxfr_mapname, yp_errno == YP_TRUE ?
+ "map has order 0" :
+ ypxfrerr_string((ypxfrstat)yp_errno));
+ ypxfr_exit(YPXFR_YPERR,ypxfr_temp_map);
+ }
+
+ if (ypxfr_order != ypxfr_skew_check)
+ ypxfr_exit(YPXFR_SKEW,ypxfr_temp_map);
+
+ /*
+ * Send a YPPROC_CLEAR to the local ypserv.
+ */
+ if (ypxfr_clear) {
+ char in = 0;
+ char *out = NULL;
+ int stat;
+ if ((stat = callrpc("localhost",YPPROG,YPVERS,YPPROC_CLEAR,
+ (xdrproc_t)xdr_void, (void *)&in,
+ (xdrproc_t)xdr_void, (void *)out)) != RPC_SUCCESS) {
+ yp_error("failed to send 'clear' to local ypserv: %s",
+ clnt_sperrno((enum clnt_stat) stat));
+ ypxfr_exit(YPXFR_CLEAR, ypxfr_temp_map);
+ }
+ }
+
+ /*
+ * Put the new map in place immediately. I'm not sure if the
+ * kernel does an unlink() and rename() atomically in the event
+ * that we move a new copy of a map over the top of an existing
+ * one, but there's less chance of a race condition happening
+ * than if we were to do the unlink() ourselves.
+ */
+ if (rename(ypxfr_temp_map, buf) == -1) {
+ yp_error("rename(%s,%s) failed: %s", ypxfr_temp_map, buf,
+ strerror(errno));
+ ypxfr_exit(YPXFR_FILE,NULL);
+ }
+
+ ypxfr_exit(YPXFR_SUCC,NULL);
+
+ return(1);
+}
diff --git a/libexec/ypxfr/ypxfr_misc.c b/libexec/ypxfr/ypxfr_misc.c
new file mode 100644
index 000000000000..48baf75f9f55
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_misc.c
@@ -0,0 +1,293 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+#include "ypxfr_extern.h"
+
+const char *
+ypxfrerr_string(ypxfrstat code)
+{
+ switch (code) {
+ case YPXFR_SUCC:
+ return ("Map successfully transferred");
+ break;
+ case YPXFR_AGE:
+ return ("Master's version not newer");
+ break;
+ case YPXFR_NOMAP:
+ return ("No such map in server's domain");
+ break;
+ case YPXFR_NODOM:
+ return ("Domain not supported by server");
+ break;
+ case YPXFR_RSRC:
+ return ("Local resource allocation failure");
+ break;
+ case YPXFR_RPC:
+ return ("RPC failure talking to server");
+ break;
+ case YPXFR_MADDR:
+ return ("Could not get master server address");
+ break;
+ case YPXFR_YPERR:
+ return ("NIS server/map database error");
+ break;
+ case YPXFR_BADARGS:
+ return ("Request arguments bad");
+ break;
+ case YPXFR_DBM:
+ return ("Local database operation failed");
+ break;
+ case YPXFR_FILE:
+ return ("Local file I/O operation failed");
+ break;
+ case YPXFR_SKEW:
+ return ("Map version skew during transfer");
+ break;
+ case YPXFR_CLEAR:
+ return ("Couldn't send \"clear\" request to local ypserv");
+ break;
+ case YPXFR_FORCE:
+ return ("No local order number in map -- use -f flag");
+ break;
+ case YPXFR_XFRERR:
+ return ("General ypxfr error");
+ break;
+ case YPXFR_REFUSED:
+ return ("Transfer request refused by ypserv");
+ break;
+ default:
+ return ("Unknown error code");
+ break;
+ }
+}
+
+/*
+ * These are wrappers for the usual yp_master() and yp_order() functions.
+ * They can use either local yplib functions (the real yp_master() and
+ * yp_order()) or do direct RPCs to a specified server. The latter is
+ * necessary if ypxfr is run on a machine that isn't configured as an
+ * NIS client (this can happen very easily: a given machine need not be
+ * an NIS client in order to be an NIS server).
+ */
+
+/*
+ * Careful: yp_master() returns a pointer to a dynamically allocated
+ * buffer. Calling ypproc_master_2() ourselves also returns a pointer
+ * to dynamically allocated memory, though this time it's memory
+ * allocated by the XDR routines. We have to rememver to free() or
+ * xdr_free() the memory as required to avoid leaking memory.
+ */
+char *
+ypxfr_get_master(char *domain, char *map, char *source, const int yplib)
+{
+ static char mastername[MAXPATHLEN + 2];
+
+ bzero((char *)&mastername, sizeof(mastername));
+
+ if (yplib) {
+ int res;
+ char *master;
+ if ((res = yp_master(domain, map, &master))) {
+ switch (res) {
+ case YPERR_DOMAIN:
+ yp_errno = (enum ypstat)YPXFR_NODOM;
+ break;
+ case YPERR_MAP:
+ yp_errno = (enum ypstat)YPXFR_NOMAP;
+ break;
+ case YPERR_YPERR:
+ default:
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ break;
+ }
+ return(NULL);
+ } else {
+ snprintf(mastername, sizeof(mastername), "%s", master);
+ free(master);
+ return((char *)&mastername);
+ }
+ } else {
+ CLIENT *clnt;
+ ypresp_master *resp;
+ ypreq_nokey req;
+
+ if ((clnt = clnt_create(source,YPPROG,YPVERS,"udp")) == NULL) {
+ yp_error("%s",clnt_spcreateerror("failed to \
+create udp handle to ypserv"));
+ yp_errno = (enum ypstat)YPXFR_RPC;
+ return(NULL);
+ }
+
+ req.map = map;
+ req.domain = domain;
+ if ((resp = ypproc_master_2(&req, clnt)) == NULL) {
+ yp_error("%s",clnt_sperror(clnt,"YPPROC_MASTER \
+failed"));
+ clnt_destroy(clnt);
+ yp_errno = (enum ypstat)YPXFR_RPC;
+ return(NULL);
+ }
+ clnt_destroy(clnt);
+ if (resp->stat != YP_TRUE) {
+ switch (resp->stat) {
+ case YP_NODOM:
+ yp_errno = (enum ypstat)YPXFR_NODOM;
+ break;
+ case YP_NOMAP:
+ yp_errno = (enum ypstat)YPXFR_NOMAP;
+ break;
+ case YP_YPERR:
+ default:
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ break;
+ }
+ return(NULL);
+ }
+ snprintf(mastername, sizeof(mastername), "%s", resp->peer);
+/* xdr_free(xdr_ypresp_master, (char *)&resp); */
+ return((char *)&mastername);
+ }
+}
+
+unsigned long
+ypxfr_get_order(char *domain, char *map, char *source, const int yplib)
+{
+ if (yplib) {
+ unsigned int order;
+ int res;
+ if ((res = yp_order(domain, map, &order))) {
+ switch (res) {
+ case YPERR_DOMAIN:
+ yp_errno = (enum ypstat)YPXFR_NODOM;
+ break;
+ case YPERR_MAP:
+ yp_errno = (enum ypstat)YPXFR_NOMAP;
+ break;
+ case YPERR_YPERR:
+ default:
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ break;
+ }
+ return(0);
+ } else
+ return(order);
+ } else {
+ CLIENT *clnt;
+ ypresp_order *resp;
+ ypreq_nokey req;
+
+ if ((clnt = clnt_create(source,YPPROG,YPVERS,"udp")) == NULL) {
+ yp_error("%s",clnt_spcreateerror("couldn't create \
+udp handle to ypserv"));
+ yp_errno = (enum ypstat)YPXFR_RPC;
+ return(0);
+ }
+ req.map = map;
+ req.domain = domain;
+ if ((resp = ypproc_order_2(&req, clnt)) == NULL) {
+ yp_error("%s", clnt_sperror(clnt, "YPPROC_ORDER \
+failed"));
+ clnt_destroy(clnt);
+ yp_errno = (enum ypstat)YPXFR_RPC;
+ return(0);
+ }
+ clnt_destroy(clnt);
+ if (resp->stat != YP_TRUE) {
+ switch (resp->stat) {
+ case YP_NODOM:
+ yp_errno = (enum ypstat)YPXFR_NODOM;
+ break;
+ case YP_NOMAP:
+ yp_errno = (enum ypstat)YPXFR_NOMAP;
+ break;
+ case YP_YPERR:
+ default:
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ break;
+ }
+ return(0);
+ }
+ return(resp->ordernum);
+ }
+}
+
+int
+ypxfr_match(char *server, char *domain, char *map, char *key,
+ unsigned long keylen)
+{
+ ypreq_key ypkey;
+ ypresp_val *ypval;
+ CLIENT *clnt;
+ static char buf[YPMAXRECORD + 2];
+
+ bzero(buf, sizeof(buf));
+
+ if ((clnt = clnt_create(server, YPPROG,YPVERS,"udp")) == NULL) {
+ yp_error("failed to create UDP handle: %s",
+ clnt_spcreateerror(server));
+ return(0);
+ }
+
+ ypkey.domain = domain;
+ ypkey.map = map;
+ ypkey.key.keydat_len = keylen;
+ ypkey.key.keydat_val = key;
+
+ if ((ypval = ypproc_match_2(&ypkey, clnt)) == NULL) {
+ clnt_destroy(clnt);
+ yp_error("%s: %s", server,
+ clnt_sperror(clnt,"YPPROC_MATCH failed"));
+ return(0);
+ }
+
+ clnt_destroy(clnt);
+
+ if (ypval->stat != YP_TRUE) {
+ xdr_free((xdrproc_t)xdr_ypresp_val, ypval);
+ return(0);
+ }
+
+ xdr_free((xdrproc_t)xdr_ypresp_val, ypval);
+
+ return(1);
+}
diff --git a/libexec/ypxfr/ypxfrd_getmap.c b/libexec/ypxfr/ypxfrd_getmap.c
new file mode 100644
index 000000000000..62bd881f1a7d
--- /dev/null
+++ b/libexec/ypxfr/ypxfrd_getmap.c
@@ -0,0 +1,144 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <rpcsvc/ypxfrd.h>
+#include <rpcsvc/yp.h>
+#include <rpc/rpc.h>
+#include <sys/uio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "ypxfr_extern.h"
+
+static int fp = 0;
+
+static bool_t
+xdr_my_xfr(register XDR *xdrs, xfr *objp)
+{
+ while (1) {
+ if (!xdr_xfr(xdrs, objp))
+ return(FALSE);
+ if (objp->ok == TRUE) {
+ if (write(fp, objp->xfr_u.xfrblock_buf.xfrblock_buf_val,
+ objp->xfr_u.xfrblock_buf.xfrblock_buf_len) == -1) {
+ yp_error("write failed: %s", strerror(errno));
+ return(FALSE);
+ }
+ }
+ xdr_free((xdrproc_t)xdr_xfr, (char *)objp);
+ if (objp->ok == FALSE) {
+ switch (objp->xfr_u.xfrstat) {
+ case(XFR_DONE):
+ return(TRUE);
+ break;
+ case(XFR_READ_ERR):
+ yp_error("got read error from rpc.ypxfrd");
+ return(FALSE);
+ break;
+ case(XFR_ACCESS):
+ yp_error("rpc.ypxfrd couldn't access the map");
+ return(FALSE);
+ break;
+ case(XFR_DENIED):
+ yp_error("access to map denied by rpc.ypxfrd");
+ return(FALSE);
+ break;
+ case(XFR_DB_TYPE_MISMATCH):
+ yp_error("client/server DB type mismatch");
+ return(FALSE);
+ break;
+ case(XFR_DB_ENDIAN_MISMATCH):
+ yp_error("client/server byte order mismatch");
+ return(FALSE);
+ break;
+ default:
+ yp_error("got unknown status from rpc.ypxfrd");
+ return(FALSE);
+ break;
+ }
+ }
+ }
+}
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+
+int
+ypxfrd_get_map(char *host, char *map, char *domain, char *tmpname)
+{
+ CLIENT *clnt;
+ struct ypxfr_mapname req;
+ struct xfr resp;
+ struct timeval timeout = { 0, 25 };
+ int status = 0;
+
+ req.xfrmap = map;
+ req.xfrdomain = domain;
+ req.xfrmap_filename = "";
+ req.xfr_db_type = XFR_DB_BSD_HASH; /*
+ req.xfr_byte_order = XFR_ENDIAN_ANY; * Berkeley DB isn't
+ * byte-order sensitive.
+ */
+
+ bzero((char *)&resp, sizeof(resp));
+
+ if ((clnt = clnt_create(host, YPXFRD_FREEBSD_PROG,
+ YPXFRD_FREEBSD_VERS, "tcp")) == NULL) {
+ return(1);
+ }
+
+ if ((fp = open(tmpname, O_RDWR|O_CREAT, PERM_SECURE)) == -1) {
+ clnt_destroy(clnt);
+ yp_error("couldn't open %s: %s", tmpname, strerror(errno));
+ return(1);
+ }
+
+ if (clnt_call(clnt,YPXFRD_GETMAP,
+ (xdrproc_t)xdr_ypxfr_mapname, (char *)&req,
+ (xdrproc_t)xdr_my_xfr, (char *)&resp,
+ timeout) != RPC_SUCCESS) {
+ yp_error("%s", clnt_sperror(clnt,"call to rpc.ypxfrd failed"));
+ status++;
+ unlink(tmpname);
+ }
+
+ clnt_destroy(clnt);
+ close(fp);
+ return(status);
+}